Improving Key Expiration in Redis
blog.twitter.com
blog.twitter.com
Do you have any insight in to why there's the drop in performance on the problematic line in the article?
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
The post focused a lot of microarchitectural issues, but I saw no mention of much bigger fish: is it really the case that the if statement always evaluates to false in Twitter’s use case? Knowing nothing about the code in question, I bet that the if condition, in fact, sometimes or always evaluates to true, and that the code doesn’t work as well when this happens.1. The || operator should short-circuit
2. I'm not sure how a jg would sanely be used to check what is effectively a boolean.
Interesting puzzle. Is it possible that asking the compiler to make this jump likely or unlikely (GCC's __builtin_expect) to be taken might help?
Like 'filesystem access is slowing us down' so we move the filesystem in to RAM. Well, it's fast now, but why are we hitting disk so much?
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
So, there's a single database... what if its `server.dbnum` is zero? This is then setting `dbs_per_call` to zero? Is `dbs_per_call` a global variable? Etc.Of course we don't have the wider code context here, but that if-statement looks like its the kind of check to exit a loop and it ought be followed by a `break` or `return` statement etc.
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
Based on http://download.redis.io/redis-stable/src/expire.c :- `server.dbnum` appears to be a global
- `timelimit_exit` is a function static, and appears to always be one of {0, 1}. It's not obvious to me that it would 'prefer' either value.
- `dbs_per_call` (along with `timelimit_exit`) is used as part of the termination condition for the induction variable.
I guess that that I don't find it too surprising (in retrospect) that this conditional is expensive. Although, I must admit that I would have suspected timelimit_exit as being the source of the latency.
I do find it interesting that branching on the value of server.dbnum is much slower than using the same value in an unconditional assignment, given that the result is presumably used shortly later in another conditional as part of the for loop.
One thing you might try is to add a superfluous fetch of the same memory location a few hundred instructions before to get it going before you hit the jump, so it's cached already by that time.