9 comments

  • erk__41 minutes ago
    I once played around with having a very large switch statement (all mac address manufactors in one switch) and in LLVM it would compile fine without optimizations, but if I turned optimizations on it would just spend around half an hour compiling it. I think GCC did it faster, but its been some time ago since I did it. In the Rust crate that was the reasons for all this I just ended up doing perfect hashing which compiled much much faster.
  • LarsKrimi9 hours ago
    &gt; The switch in sparse_8 could be simplified by dividing x by eight before the jump. Interestingly, gcc doesn&#x27;t find this simplification.<p>It could not because that would ensure that 7 values for each case would be handled by function calls instead of the default case - 0&#x2F;8 is 0, but 3&#x2F;8 is also 0
    • rep_lodsb8 hours ago
      All the values are divisible by 8, so it could do it like this:<p><pre><code> cmp eax,40 ja &lt;return 0&gt; test eax,7 jnz &lt;return 0&gt; jmp JumpTable[rax]</code></pre>
      • LarsKrimi7 hours ago
        Okay. Good point, that is quite a bit shorter than I first thought of<p>A rough benchmark of a version of that against the original shows it to be close to twice as fast for unpredictable input with GCC (4.5 ns vs 2.72 ns) on my old machine. With predictable input where x values often are the same the original version is quicker<p><pre><code> int sparse_8_jump(int x, int val) { unsigned int idx = (unsigned int)x &gt;&gt; 3; if (idx &gt; 5) return 0; if (x &amp; 7) return 0; switch (idx) { case 0: return f0(val); case 1: return f1(val); case 2: return f2(val); case 3: return f3(val); case 5: return f4(val); default: return 0; } }</code></pre>
      • ack_complete1 hour ago
        Think it could also be done with rotate, but unsure if it&#x27;d be faster:<p><pre><code> mov ecx, eax ror eax, 3 cmp eax, 5 ja &lt;return 0&gt; jmp JumpTable[rcx]</code></pre>
    • Findecanor8 hours ago
      Reducing the range would have been useful to reduce the size of a jump table, but when you have a decision tree then it does not matter.<p>... unless the size of immediate parameters would have affected code size. In the example code however, all constants fit in an `imm8` parameter to `cmp`.
  • rurban9 hours ago
    What a shame for clang and gcc! They still don&#x27;t implement perfect hashes for large sparse switch tables. I did that recently for my rcc C compiler, and it was just a single day of work. Esp. beneficial for unicode work. The LLVM patch was rejected a decade ago. Gcc has several unicode patches sitting in branches, which would also need that
    • jcranmer3 hours ago
      Compiler optimizations are a trade-off between compile time, runtime, and code size, mediated by the availability of developer time.<p>Perfect hashing for switch tables is useful mostly for large, sparse switches, but <i>hot</i> large, sparse switches are rare, and the closest examples I can think of (e.g., parse tables) are themselves likely generated from other tools, which could itself be changed in lieu of the compiler. It really doesn&#x27;t come across as a useful optimization to try implementing in a compiler.
      • bobmcnamara3 hours ago
        It comes up in emulators and VMs, and indeed those are often tool generated.
        • flohofwoe1 hour ago
          Hmm, IME at least in emulators the switch tables usually have enough &#x27;structure&#x27; that GCC or Clang have no trouble turning them into jump tables.
    • nh27 hours ago
      Maybe LLVM doesn&#x27;t have time for performance improvements due to still reading through 30000 open issues, which also include miscompilations leading to incorrect results at runtime?<p>For example, my bug report that `+= 2` is broken has not been acknowledged (read?) by anyone for 3 years:<p><pre><code> const size_t n = 13; &#x2F;&#x2F; triggers the issue #pragma omp parallel for for (uint32_t i = 0; i &lt; n; i += 2) { &#x2F;&#x2F; counts PAST 13! </code></pre> <a href="https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;issues&#x2F;70687" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;issues&#x2F;70687</a>
      • rurban6 hours ago
        That&#x27;s true. restrict is also still broken there. 30000 open issue is even worse than gcc
      • stefan_4 hours ago
        #pragma omp<p>Ahh, there it is! This issue isn&#x27;t being tended to because it will resolve itself with the imminent death of OpenMP.
        • jamiejquinn1 hour ago
          Now there&#x27;s a bold statement! What makes you think OpenMP is on the way out?
    • RossBencina8 hours ago
      Would like to hear more about the performance characteristics. Do you perfect hash to a contiguous range and then use a jump table, or do you perfect hash directly to relative offsets to basic blocks?
      • rurban5 hours ago
        See my docs. There are several cases: <a href="https:&#x2F;&#x2F;github.com&#x2F;rurban&#x2F;rcc&#x2F;blob&#x2F;main&#x2F;docs&#x2F;rcc.md#-o1-and-up-the-peephole-optimizer-srcoptc" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;rurban&#x2F;rcc&#x2F;blob&#x2F;main&#x2F;docs&#x2F;rcc.md#-o1-and-...</a>
        • Sopel4 hours ago
          why is it so slow? it&#x27;s around 50x slower than I would have expected for numbers <a href="https:&#x2F;&#x2F;github.com&#x2F;ConstexprCore&#x2F;perfect_hash" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;ConstexprCore&#x2F;perfect_hash</a>
        • garaetjjte5 hours ago
          [flagged]
    • tester7569 hours ago
      I&#x27;d bet because that&#x27;s probably no better, that&#x27;s it.<p>&gt;The LLVM patch was rejected a decade ago<p>And what was the reasoning?
      • ahartmetz8 hours ago
        Also you could try again, many things may have changed since then including who maintains the code.
        • saagarjha6 hours ago
          It seems like someone did: <a href="https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;issues&#x2F;96971" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;issues&#x2F;96971</a>. However the performance implications of this was not well explored there
          • ahartmetz4 hours ago
            If the problem with the previous attempt was the same as with the more recent one, well duh. Just because this approach is new and clever doesn&#x27;t mean it should always be used. It should be a last resort if cost estimates for other approaches are higher than some threshold where perfect hashing is likely to be cheaper.
      • rurban8 hours ago
        [flagged]
        • saagarjha6 hours ago
          In this case you&#x27;re not doing much better.
          • rurban5 hours ago
            I strongly reject that.
            • saagarjha4 hours ago
              Then provide a link to your submission and we can see this for ourselves.
  • tsukikage9 hours ago
    Why the indirection? - why not have the jump table reference f0..fX directly rather than jumping to a jump?
    • Findecanor7 hours ago
      That optimisation is simply missing.<p>Each `jmp` to f0..fX is an optimisation of a `call` and a `ret`, and probably done in a separate pass after the switch-case code has already been expanded.<p>The `mov edi, esi` at the top of the function is also an optimisation, most likely done during the register allocation pass. This was only possible because all functions take the same value in the same register. Otherwise, there would probably have been also a `mov` before each of the calls.
      • andikleen26 hours ago
        gcc unfortunately cannot do this (and also not the related optimization of using word&#x2F;byte offsets for the jump table, which LLVM can do) because it only has approximate estimation of code size. If it got the code size wrong it would be a mis-compilation. This is related to it not using an integrated assembler.
    • yksalun8 hours ago
      [flagged]
  • p-t3 hours ago
    I assume 34 in the compact_7() one was supposed to be 35, and edx was supposed to be used at some point in the bittest() one (unless i&#x27;m missing something lol)
  • Andi8 hours ago
    I remember having implemented similar things in JavaScript (by generating code), for immutable Maps and Sets - no significant performance gain in modern engines. For regular expressions of complex unicode ranges, the speedup was around 2x!
  • sylware9 hours ago
    In order to peak into hardware speculation&#x2F;BTB, having a look atswitch lowering seems a good way. Big switches will go into serious convolution to please that hardware since by definition this is a source of a lot of branch mispredicitions.
    • ahartmetz8 hours ago
      There is surprisingly little about the proper feeding of advanced hardware in that code, two or three constants in cost calculations of which one or two are hardware-dependent.<p>It is still possible that a later pass arranges jump targets to avoid false sharing of their entries (addresses) in prediction mechanisms and such.
      • sylware7 hours ago
        Just look at the assembly output of significantly sized switch statements (with good random values): it&#x27;s scary :)<p>Obvsiously engaging in pleasing as much as it can speculative&#x2F;BTB hardware (to the cost of memory tables!)
  • arsalan1710 hours ago
    This one was interesting, especially its name :)
  • yarkwright5 hours ago
    [flagged]