Why Jitter?
When a periodic task fires at a fixed interval β a retry loop, a polling mechanism, a heartbeat β that regularity often becomes its own problem. Synchronized bursts from multiple clients (the thundering herd), queue pile-ups, and statistical profiling are all side effects of fixed delays.
The solution: added jitter. If the wait time varies on every call, the pattern breaks, load flattens, and predictability disappears.
In this post we walk through a pure x64 Assembly function that applies a Linear Congruential Generator (LCG) scramble over an rdtsc entropy seed, then sleeps for a random duration in the [100ms, 1000ms) range.
π© The Full Function: _lcg_jitter
|
|
π¦ Part 1: Register Preservation
|
|
When this function is called, the caller’s register state must remain untouched. So every general-purpose register is pushed onto the stack at the top and restored in reverse order at the bottom.
Note: Under the System V AMD64 ABI,
rbx,rbp, andr12βr15are callee-saved; the rest are caller-saved. Both sets are saved here β maximum safety, no assumptions about the call site.
β±οΈ Part 2: Entropy Source β rdtsc
|
|
The rdtsc (Read Time-Stamp Counter) instruction reads the processor’s 64-bit cycle counter accumulated since boot. The low 32 bits land in EAX, the high 32 in EDX.
This value changes rapidly β a reliable snapshot of hardware state. However it isn’t used raw: lower bits can carry detectable patterns on some microarchitectures. The LCG step that follows solves this.
π² Part 3: LCG Scramble
|
|
These two lines implement the classic LCG formula from Numerical Recipes:
X_{n+1} = (a Γ X_n + c) mod 2^32
| Parameter | Value | Source |
|---|---|---|
a |
1664525 | Numerical Recipes |
c |
1013904223 | Numerical Recipes |
m |
2^32 | Implicit (32-bit overflow) |
imul eax, eax, 1664525 multiplies EAX by the LCG multiplier β overflow is intentional, giving us modular arithmetic for free.
add eax, 1013904223 adds the increment c.
The result: the raw TSC value is transformed into a pseudo-random number with a statistically flatter distribution.
β Part 4: Range Calculation
|
|
div ecx divides the 64-bit value EDX:EAX by ECX:
EAXβ quotient (discarded)EDXβ remainder β falls in[0, 900_000_000)
Adding 100_000_000 shifts the window:
[0, 900_000_000) + 100_000_000 = [100_000_000, 1_000_000_000)
In wall-clock terms: a uniformly distributed random sleep between 100 ms and 1000 ms.
π΄ Part 5: Sleeping with sys_nanosleep
|
|
nanosleep(2) expects a pointer to struct timespec:
|
|
The struct is built directly on the stack:
[rsp]βtv_sec = 0(sub-second sleep only)[rsp+8]βtv_nsec = EDX(our computed random value)
The sub rsp, 32 reservation keeps the stack 16-byte aligned and provides comfortable headroom. Only 16 bytes are strictly necessary for one timespec, so 32 bytes is a conservative over-allocation β benign and safe.
π Distribution Analysis
The LCG + modular arithmetic combination produces a theoretically uniform distribution across the target window:
Range: [100ms, 1000ms)
Expected mean: ~550ms
Standard deviation: ~260ms
LCG period: 2^32 β 4.29 billion calls before cycle repeats
For most use cases the period is far longer than needed, so cycle repetition is not a practical concern.
β οΈ Limitations and Alternatives
| Concern | This Implementation | Alternative |
|---|---|---|
| Cryptographic security | β Not suitable | getrandom(2) syscall |
| Multi-threaded safety | β οΈ No shared state to protect here | Thread-local seed for stateful LCG |
| Reproducibility | β Fix the seed | Pass a constant initial value |
| Precision | β Nanosecond resolution | Sufficient for all timing jitter use cases |
For non-cryptographic, non-security-sensitive jitter this implementation hits all the marks cleanly.
Conclusion
This small function elegantly combines three ideas:
- Hardware entropy β
rdtscgives a unique starting point on every call - LCG scrambling β a two-instruction transform that flattens the distribution
- Direct syscall β kernel scheduling infrastructure accessed without any library overhead
The outcome: externally irregular, internally deterministic β a lightweight timing jitter engine that lives entirely within the CPU and the kernel.
Github: https://github.com/JM00NJ/Sectionless-Craft/tree/main/Jitter
Stay Coded!