Just came across a good paper that discusses these issues: http://arxiv.org/pdf/1509.05053v1 The approach it from a different angle, but come to similar conclusions: Our conclusion is contrary to our expectations and to previous findings and goes against conventional wisdom, which states that accesses to RAM are slow, and should be minimized. A more accurate statement, that accounts for our findings, is that accesses to RAM have high latency and this latency needs to be mitigated. ... Our results show that, in a processor that has speculative execution, one can obtain many of the benefits of runahead even on processors that do not specifically implement it. While this may not be news to hardware experts, to the best of our knowledge, we are unaware of any algorithms (or even implementations) that deliberately make use of it. I would suggest this is a very fruitful direction to pursue.
Array Layouts for Comparison-Based Searching
Paul-Virak Khuong, Pat Morin
2015 · arXiv
- HN score
- 1
- Mentions
- 9
- Readers
- 8
- Months
- 7
From the abstract
We attempt to determine the best order and search algorithm to store $n$ comparable data items in an array, $A$, of length $n$ so that we can, for any query value, $x$, quickly find the smallest value in $A$ that is greater than or equal to $x$. In particular, we consider the important case where there are many such queries to the same array, $A$, which resides entirely in RAM. In addition to the obvious sorted order/binary search combination we consider the Eytzinger (BFS) layout normally used for heaps, an implicit B-tree layout that generalizes the Eytzinger layout, and the van Emde Boas layout commonly used in the cache-oblivious algorithms literature. After extensive testing and tuning on a wide variety of modern hardware, we arrive at the conclusion that, for small values of $n$, sorted order, combined with a good implementation of binary search is best. For larger values of $n$, we arrive at the surprising conclusion that the Eytzinger layout is usually the fastest. The latter conclusion is unexpected and goes counter to earlier experimental work by Brodal, Fagerberg, and Jacob (SODA~2003), who concluded that both the B-tree and van Emde Boas layouts were faster than the Eytzinger layout for large values of $n$. Our fastest C++ implementations, when compiled, use conditional moves to avoid branch mispredictions and prefetching to reduce cache latency.
Metadata: arXiv. Abstracts retain their original authors’ rights.
In the conversation
Original HN comments, with labels from simple text rules. A label describes the whole comment and may miss context or irony. Read the discussion to make up your own mind. About the labels
9 selected excerpts · 9 total mentions
That one’s cache-oblivious so nominally the right answer if you don’t know how your cache works or have to deal with several of them at once, but the constants are bad enough to make it impractical[1,2]. Personally I’ve been on the lookout for a good vEB implementation for a while: there’s like one of them on the whole Internet, and it does things like recurse while figuring out where the next node down is, so no wonder it’s slow; and when I look at the definitions myself I can’t really figure out a faster way of doing it either. [1] http://arxiv.org/abs/1509.05053 [2] https://espindo.la/posts/array-layouts.html
auto length = last - first; while (length > 0) { auto half = length / 2; if (comp(first[half], value)) { // length - half equals half + length % 2 first += length - half; } length = half; } return first; can be restructured with a conditional `first += half` and instead `length -= half` (see, e.g., Listing 2 in https://arxiv.org/abs/1509.05053). Doing it this way requires one final comparison when `length > 0` on entry, because the while condition is `length > 1`, but moves the subtraction off the critical path.
Very interesting post. The best results I knew of for static ordered search [1] were from the 2015 Khuong-Morin paper [2], which is not cited in the article but the author cites it in the previous post, and in their benchmarks Eytzinger+prefeching performed best. The improvement on top of that is pretty impressive. At the larger sizes, the latencies are basically 1 cache miss (~50ns). However, I have some comment on the benchmark, the iterations have no loop dependency on the previous result, so the CPU is allowed to pipeline some loads. This is rarely representative of real workloads, where the key being searched comes from other computations, so this is more a measure…
Read the full comment
of highest possible reciprocal throughput than actual latency. Would be interesting to see what happens if they make the key being searched a function of the result of the previous lookup. (EDIT: as gfd below points out, the article actually discusses this!) Also some of the tricks only work for 32-bit keys, which allow the vectorized search in nodes. For larger keys, different layouts may still be preferable. [1] Ordered search is when you need to look up keys that are not in the dataset, and find predecessor/successor. If you only do exact lookups, a hashtable will almost certainly perform better, though it would require a bit more space. [2] https://arxiv.org/pdf/1509.05053.pdf
> That is because you no longer need to compute a mid-point at every iteration, those are now pre-computed in the ordered array. This is called the Eytzinger layout [1]. [1] https://arxiv.org/abs/1509.05053
The more recent results (by the same author) is this paper: https://arxiv.org/pdf/1509.05053.pdf The conclusion is still that CMOV is best, but once the data stops fitting in the cache there's alternatives worth considering.
Reminds me of this paper on binary search, which also shows a speed up using branch-free comparisons. "Array Layouts for Comparison-Based Searching" https://arxiv.org/pdf/1509.05053.pdf
Have a look at https://arxiv.org/abs/1509.05053 from pkhuong. They discuss this issue and other variants that paper. So e.g. branchless binary search, the branchy binary search + speculative execution as a form of prefetching, branchless binary search with explicit prefetching.
I try to refrain from purely negative commentary on articles, but yuck! How can one hope to say anything useful about "memory performance" without once using the terms "latency" or "bandwidth"? Memory performance is getting higher in the same way that processor performance is going up: http://www.corsair.com/en-us/blog/2015/september/ddr3_vs_ddr... Latency is to frequency as bandwidth is to parallelism. Single core CPU frequencies are relatively stable, but parallelism offers the opportunity to get more done each cycle. Latency for random access is holding quite steady, but caches are getting bigger and faster, and bandwidth is going up. The key is figuring out how to…
Read the full comment
write software that takes advantage of spatial locality and available bandwidth rather than getting choked by the latency. This is hard in the same way that taking advantage of multiple cores is hard: it requires a different approach, but is not a fundamental limitation. Lots of memory access isn't the problem. Long latency isn't the problem. The problem is designing your program so that it generates lots of long latency memory accesses and then grinds to a halt in the presence of this latency. I think the summary from this recent paper is spot on: Our conclusion is contrary to our expectations and to previous findings and goes against conventional wisdom, which states that accesses to RAM are slow, and should be minimized. A more accurate statement, that accounts for our findings, is that accesses to RAM have high latency and this latency needs to be mitigated. http://arxiv.org/pdf/1509.05053v1.pdf
Keep following the thread.
More in DatabasesMore from the same topics.
The Case for Learned Index Structures
Tim Kraska, Alex Beutel, Ed H. Chi +2
Indexes are models: a B-Tree-Index can be seen as a model to map a key to the position of a record within a sorted array, a Hash-Index as a model to map a key to a position of a record within an unsorted array, and a BitMap-Index as a model to indicate if a data record exists or not.
A Critique of the CAP Theorem
Martin Kleppmann
The CAP Theorem is a frequently cited impossibility result in distributed systems, especially among NoSQL distributed databases.
Copy-and-Patch Compilation: A fast compilation algorithm for high-level languages and bytecode
Haoran Xu, Fredrik Kjolstad
Fast compilation is important when compilation occurs at runtime, such as query compilers in modern database systems and WebAssembly virtual machines in modern browsers.
Categorizing Variants of Goodhart's Law
David Manheim, Scott Garrabrant
There are several distinct failure modes for overoptimization of systems on the basis of metrics.