Skip to content

Commit d21d529

Browse files
authored
Optimize skiplist random level generation logic (valkey-io#2631)
Each insertion of a skiplist node requires generating a random level (via the `zslRandomLevel` function), and some commands (such as `zunionstore`) call the `zslRandomLevel` function multiple times. Therefore, optimizing `zslRandomLevel` can significantly improve the performance of these commands. The main optimization approach is as follows: 1. Original logic: Each iteration called the `random` function, with a 0.25 probability of continuing to call `random` again. In the worst-case scenario, it required up to 32 calls (though the probability of this occurring is extremely low). 2. Optimized logic: We only need to call the `genrand64_int64` function once. Each iteration uses only 2 bits of randomness, effectively achieving the equivalent of 32 iterations in the original algorithm. 3. Additionally, the introduction of `__builtin_clzll` significantly reduces CPU usage, which compiles into a single, highly efficient CPU instruction (e.g., LZCNT on x86, CLZ on ARM) on supported hardware platforms 4. Although I've explained a lot, the actual code changes are quite minimal, so just look at the code directly. --------- Signed-off-by: chzhoo <czawyx@163.com>
1 parent 298f7da commit d21d529

File tree

2 files changed

+8
-5
lines changed

2 files changed

+8
-5
lines changed

src/server.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,6 @@ typedef enum {
520520
#define SUPERVISED_UPSTART 3
521521

522522
#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */
523-
#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
524523
#define ZSKIPLIST_MAX_SEARCH 10
525524

526525
/* Append only defines */

src/t_zset.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,14 @@ void zslFree(zskiplist *zsl) {
168168
* (both inclusive), with a powerlaw-alike distribution where higher
169169
* levels are less likely to be returned. */
170170
static int zslRandomLevel(void) {
171-
static const int threshold = ZSKIPLIST_P * RAND_MAX;
172-
int level = 1;
173-
while (random() < threshold) level += 1;
174-
return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
171+
uint64_t rand = genrand64_int64();
172+
173+
/* The probability of gaining 2 additional leading zeros is 0.25.
174+
* This matches the level calculation logic perfectly: each
175+
* iteration has a 0.25 probability of increasing the level by 1.
176+
* Note: __builtin_clzll has undefined behavior when the input is 0. */
177+
int level = rand == 0 ? ZSKIPLIST_MAXLEVEL : (__builtin_clzll(rand) / 2 + 1);
178+
return level;
175179
}
176180

177181
/* Compares node and score/ele; defines zset ordering. Return value:

0 commit comments

Comments
 (0)