4 ms·
Hello antirez, Indeed, I think the pseudo-LRU approach as you describe, especially when combined with a pool, is a pragmatic caching solution for when you don'
by jonstewart 10y ago
Hello antirez,
Indeed, I think the pseudo-LRU approach as you describe, especially when combined with a pool, is a pragmatic caching solution for when you don't want to make major changes to your codebase. You found 24 bits, you dedicated a small data structure off to the side, and those two in combination are a really good approximation of LRU, without a lot of code.
I'm obviously not as familiar with the guts of redis's implementation as you are, so I don't know all the ways that keys can be stored and what the implications for caching are. Generally a caching data structure can be used that references the cached data/objects (pointers, basically), so it could be external to how you otherwise store data. That may not be the right approach with a low-level C/C++ block caching server, but that's not what redis is, either. I think you would find that adding a separate data structure would help you out, but that's just naïve advice; I don't know the guts of redis so perhaps you can't store keys in a separate structure.
I also well understand that providing real-world software is an exercise in compromises. However, I recently discovered how horrible an existing LRU implementation performed in practice for my application (digital forensics) and spent considerable resources exploring alternatives. And what we really found out during all of this is that instrumentation and testing under a diverse set of input is necessary in order to make an informed decision. For some input, LRU was gangbusters. For others, it was lousy. I've also learned that sometimes it's necessary to put pragmatism aside and go off and implement the ideal approach; it can pay off.
Cheers,
Jon
- eternalban 10y agoThe "ideal approach" to caching, Jon, is an oracle. There is no such thing as an "ideal cache eviction algorithm", just FYI.
- jonstewart 10y agoAlso known as Bélády's algorithm.
- eternalban 10y agoThis is my final note (and I am not downvoting you, fyi) but you said "implement the ideal approach". Magic? Let's see the code. I checked your github repo and can't find this "implementation".
- jonstewart 10y agoThe last sentence I meant more generally than the domain of cache algorithms, setting up "ideal" as a contrast to "pragmatic", since I'm taking flak for being "academic" (haha, you should have seen my grades). One lesson I've learned in my career is that there are instances where it pays to take some time, see what the research says, experiment, and then refactor. It takes a lot of time, but the best software is also long-lived so it can pay to take the time to make one's software the best. antirez has 24 bits to play with and doesn't want to make major changes, probably also doesn't have a lot of spare time, and that's his choice. But I am also aware of the oracle algorithm.
- lossolo 10y agoYou still don't understand what Redis is and what antirez wrote in first reply to you. Your "major changes" will have impact on my use cases. Redis is not only used for caching, I am not using Redis in production for caching and many other people also do not use Redis for caching.
- eternalban 10y ago> setting up "ideal" as a contrast to "pragmatic" Have to retract the last comment['s promise of final note] and (academically) note here that "ideal" and "optimal" are distinct notions in computer science. A provably "optimal" algorithm is implementable. An "ideal" that requires magical facilities is a trope to make a point and spare one from attempting the impossible.
- antirez 10y agoHello Jon, adding an additional data structure has the problem of slowing down the ability to serve queries, and there is a memory overhead problem as well. A lot of time spent when modifying the eviction policy is to make sure there are on speed regressions. Moreover in Redis you can switch on and off the "maxmemory" setting and change eviction policy at runtime: when it's off no resources should be needed, when it is turned on, no pause should happen. So it is very hard to find both the time and space resources in order to implement the perfect solution. However the blog post in its latter part describes the LFU implementation that is now into Redis (unstable branch). This implementation was derived with just a few days of work, I had only that much to work at the problem, but was obtained writing a few simulation programs and traces in order to check the behavior, so I don't think it's an "obvious loser". For instance I can imagine different workloads where it can work better compared to LIRS. For example when you get accesses in "spaced bursts", LIRS, by remembering only the last two access times, may get confused about what data is really worthwhile to take. In teams like your where you have a specific problem to address, and you can test N approaches, you can select a specific algorithm that works the best for you, but in Redis it would be a lot better if there is a more adaptive approach. Intuitively, and by the traces I was able to run on it, it looks like that a logarithmic counter and the halving timer while not always optimal should be able to provide decent performances under different access loads, since in some way, it really is an LFU algorithm (in that the frequency of accesses is retained by the algorithm in a direct way, instead of being guessed by secondary data), so it should not be trivially fooled by odd access patterns. Also being it tunable in the two main parameters, it should be possible for users to improve it for application specific loads. I understand your frustration with LRU implementations, but given you care and have experience on this stuff, I invite you to take a look at the performances of the new Redis algorithm, maybe after all it could work better than expected, or you could be able to do trivial changes to it in order to significantly improve how it works, so that all we could benefit. Regards. EDIT: p.s. if somebody is inclined to test it, in 24 bits, I'm sure it's possible to implement some kind of LIRS approximation easily, by using two 12 bits timestamps. The problem with a 12 bit timestamp is that all objects having more than 1 hours are ranked the same, but in the case of a cache like Redis, it could be acceptable perhaps... Anyway to try different approaches in the unstable branch is just as hard as writing a few lines of code AFAIK.