Skip to content
SanmaycePublic

About

Fastest Scalar&Vector memmem() in C?!

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

42 Commits

Folders and files

Repository files navigation

Railgun

Fastest Scalar&Vector memmem() in C?!

eb0af275-eb32-4d44-827a-bac00cbc284b

This is the C sourcecode of Railgun_Trolldom and Railgun_Nyotengu memmem-like functions.

Also, a benchmark is included juxtaposing GLIBC memmem() to both Railguns, here come the results on my laptop running Fedora 39 Linux:

r3

I plan to boost the vector variant by replacing the 2,3 bytes Needle Length cases with vectorized code, now they are scalar...

Did the above, more in-depth benchmarking (including DNA corpus):
https://forums.fedoraforum.org/showthread.php?334054-DeepSeek-coding-the-incoming-AI-assistants-under-pressure&p=1891046#post1891046

Corpus 'SRC':

Compiler used: CLANG 17.0.6 (-O3 -mavx2)
Test machine: ThinkPad L490, 64GB DDR4, i7-8565U
Haystack: linux-6.6.1.tar (~1.3GB)
Note: memmem_chatGPT uses BMH "bad-character" shift.
+---------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
| Search Term (Length)                  | Hits Found | memmem_chatGPT (ms) | GLIBC_memmem (ms) | Railgun_Scalar (ms) | Railgun_Vector (ms) | musl_memmem (ms) |
+---------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
| "Linus Torvalds" (14)                 | 609        | 420.984             | 163.875           | 114.899             | 99.039              | 519.765          |
| "Linux Torvalds" (14)                 | 3          | 414.477             | 162.387           | 111.148             | 98.536              | 530.717          |
| ");" (2)                              | 3746566    | 2221.938            | 886.321           | 715.871             | 123.976             | 635.534          |
| "  " (2)                              | 167559501  | 6689.970            | 1529.983          | 1072.138            | 1077.036            | 1516.452         |
| "zoo" (3)                             | 485        | 1462.869            | 483.407           | 696.596             | 89.969              | 686.752          |
| "int" (3)                             | 1936666    | 1605.918            | 588.149           | 757.421             | 110.929             | 679.830          |
| "fast" (4)                            | 17353      | 1196.216            | 360.294           | 332.889             | 99.408              | 687.099          |
| "Linux" (5)                           | 15982      | 901.836             | 330.509           | 266.847             | 99.922              | 478.874          |
| "btree" (5)                           | 11426      | 979.820             | 308.749           | 260.635             | 98.751              | 602.234          |
| "struct" (6)                          | 2311867    | 891.359             | 339.537           | 2638.379            | 251.426             | 606.591          |
| "include " (8)                        | 386168     | 758.692             | 279.469           | 559.476             | 135.691             | 781.316          |
| "<stdio.h>" (9)                       | 1210       | 520.690             | 196.464           | 141.006             | 96.712              | 382.388          |
| "magnificient" (12)                   | 0          | 455.998             | 162.529           | 126.167             | 98.957              | 417.653          |
| "consumer->node" (14)                 | 2          | 417.462             | 164.796           | 119.532             | 132.856             | 380.881          |
| "irq_bypass_unregister_consumer" (30) | 5          | 242.009             | 126.106           | 86.300              | 107.586             | 301.109          |
| "void irq_bypass_unre...oducer)" (73) | 1          | 175.912             | 144.484           | 77.297              | 89.969              | 226.835          |
+---------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
Note: The last Needle is: "void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)"

Ratio Calculations (Railgun_Scalar vs. musl_memmem)

For each row, ratio = musl_memmem time / Railgun_Scalar time:

"Linus Torvalds" (14): 519.765 / 114.899 = 4.523 ≈ Railgun_Scalar is 4.52x faster
"Linux Torvalds" (14): 530.717 / 111.148 = 4.774 ≈ Railgun_Scalar is 4.77x faster
");" (2): 635.534 / 715.871 = 0.887 ≈ 0.89x faster
" " (2): 1516.452 / 1072.138 = 1.414 ≈ Railgun_Scalar is 1.41x faster
"zoo" (3): 686.752 / 696.596 = 0.986 ≈ 0.99x faster
"int" (3): 679.830 / 757.421 = 0.897 ≈ 0.90x faster
"fast" (4): 687.099 / 332.889 = 2.063 ≈ Railgun_Scalar is 2.06x faster
"Linux" (5): 478.874 / 266.847 = 1.794 ≈ Railgun_Scalar is 1.79x faster
"btree" (5): 602.234 / 260.635 = 2.310 ≈ Railgun_Scalar is 2.31x faster
"struct" (6): 606.591 / 2638.379 = 0.230 ≈ 0.23x faster
"include " (8): 781.316 / 559.476 = 1.396 ≈ Railgun_Scalar is 1.40x faster
"<stdio.h>" (9): 382.388 / 141.006 = 2.712 ≈ Railgun_Scalar is 2.71x faster
"magnificient" (12): 417.653 / 126.167 = 3.310 ≈ Railgun_Scalar is 3.31x faster
"consumer->node" (14): 380.881 / 119.532 = 3.186 ≈ Railgun_Scalar is 3.19x faster
"irq_bypass_unregister_consumer" (30): 301.109 / 86.300 = 3.489 ≈ Railgun_Scalar is 3.49x faster
"void irq_bypass_unre...oducer)" (73): 226.835 / 77.297 = 2.934 ≈ Railgun_Scalar is 2.93x faster 

Corpus 'DNA':

Compiler used: CLANG 17.0.6 (-O3 -mavx2)
Test machine: ThinkPad L490, 64GB DDR4, i7-8565U
Haystack: GCF_000001405.40_GRCh38.p14_genomic.fna (~3.1GB)
Note: memmem_chatGPT uses BMH "bad-character" shift.
+--------------------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
| Search Term (Length)                             | Hits Found | memmem_chatGPT (ms) | GLIBC_memmem (ms) | Railgun_Scalar (ms) | Railgun_Vector (ms) | musl_memmem (ms) |
+--------------------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
| "TT" (2)                                         | 166135260  | 9705.412            | 3759.067          | 3041.696            | 1494.914            | 3880.618         |
| "CCGA" (4)                                       | 1086495    | 4920.447            | 2220.296          | 2741.837            | 342.207             | 1708.231         |
| "GACC" (4)                                       | 4412976    | 5216.348            | 2603.599          | 6618.800            | 530.432             | 1787.914         |
| "CCCCGGGA" (8)                                   | 12213      | 2668.236            | 1297.836          | 907.977             | 662.409             | 3157.571         |
| "GCGGACCCC" (9)                                  | 1470       | 2593.179            | 1446.416          | 1181.289            | 349.091             | 2900.938         |
| "GTTTCTCAGGTTGAGACTCCAGTGGCCTCCCAGCTGCTGGC" (41) | 1          | 2105.935            | 813.992           | 285.679             | 1044.218            | 2224.563         |
| "TTTTAGTGTAGGTCATTCTAGAAAGCCT...TTCTCAAGGT" (80) | 1          | 1943.023            | 689.646           | 270.119             | 342.207             | 1708.231         |
+--------------------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
Note: The last Needle is: "TTTTAGTGTAGGTCATTCTAGAAAGCCTGGGTTCAGGAATTTTGAATGCTACTTCTTCATAGGGTAGCGGTTCTCAAGGT"

Ratio Calculations (Railgun_Scalar vs. musl_memmem)

For each row, ratio = musl_memmem time / Railgun_Scalar time:

"TT" (2): 3880.618 / 3041.696 = 1.2758 ≈ Railgun_Scalar is 1.28x faster
"CCGA" (4): 1708.231 / 2741.837 = 0.6232 ≈ 0.62x faster
"GACC" (4): 1787.914 / 6618.800 = 0.2701 ≈ 0.27x faster
"CCCCGGGA" (8): 3157.571 / 907.977 = 3.4778 ≈ Railgun_Scalar is 3.48x faster
"GCGGACCCC" (9): 2900.938 / 1181.289 = 2.4556 ≈ Railgun_Scalar is 2.46x faster
"GTTTCTCAGGTTGAGACTCCAGTGGCCTCCCAGCTGCTGGC" (41): 2224.563 / 285.679 = 7.7875 ≈ Railgun_Scalar is 7.79x faster
"TTTTAGTGTAGGTCATTCTAGAAAGCCT...TTCTCAAGGT" (80): 1708.231 / 270.119 = 6.3239 ≈ Railgun_Scalar is 6.32x faster

Corpus 'TXT':

Compiler used: CLANG 17.0.6 (-O3 -mavx2)
Test machine: ThinkPad L490, 64GB DDR4, i7-8565U
Haystack: Machine-Learning_Urban_Dictionary_Definitions_Corpus_(1999_-_May-2016).words.json (~1.8GiB)
Note: memmem_chatGPT uses BMH "bad-character" shift.
+---------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
| Search Term (Length)                  | Hits Found | memmem_chatGPT (ms) | GLIBC_memmem (ms) | Railgun_Scalar (ms) | Railgun_Vector (ms) | musl_memmem (ms) |
+---------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+
| "to" (2)                              | 5416776    | 3401.056            | 1180.169          | 949.869             | 167.774             | 1011.336         |
| "http" (4)                            | 4418092    | 1715.765            | 602.959           | 3844.025            | 372.715             | 936.776          |
| "mono" (4)                            | 9568       | 1690.800            | 528.203           | 456.745             | 131.491             | 940.014          |
| "struct" (6)                          | 20561      | 1166.789            | 356.405           | 313.315             | 141.153             | 992.716          |
| "thought" (7)                         | 45138      | 1013.296            | 351.161           | 320.558             | 153.091             | 836.498          |
| "intuitive" (9)                       | 481        | 836.118             | 279.763           | 229.047             | 130.794             | 833.949          |
| "obsessiveness" (13)                  | 22         | 585.256             | 203.262           | 164.672             | 131.908             | 586.452          |
| "An obnoxious loud drunkard who" (30) | 1          | 407.660             | 183.897           | 117.743             | 136.747             | 650.994          |
+---------------------------------------+------------+---------------------+-------------------+---------------------+---------------------+------------------+

Ratio Calculations (Railgun_Scalar vs. musl_memmem)

For each row, ratio = musl_memmem time / Railgun_Scalar time:

"to" (2): 1011.336 / 949.869 = 1.0647 ≈ Railgun_Scalar is 1.06x faster
"http" (4): 936.776 / 3844.025 = 0.2437 ≈ 0.24x faster
"mono" (4): 940.014 / 456.745 = 2.0579 ≈ Railgun_Scalar is 2.06x faster
"struct" (6): 992.716 / 313.315 = 3.1685 ≈ Railgun_Scalar is 3.17x faster
"thought" (7): 836.498 / 320.558 = 2.6094 ≈ Railgun_Scalar is 2.61x faster
"intuitive" (9): 833.949 / 229.047 = 3.6410 ≈ Railgun_Scalar is 3.64x faster
"obsessiveness" (13): 586.452 / 164.672 = 3.5616 ≈ Railgun_Scalar is 3.56x faster
"An obnoxious loud drunkard who" (30): 650.994 / 117.743 = 5.5278 ≈ Railgun_Scalar is 5.53x faster

So, another hype, to me, Two-Way algorithm is no match to the Railgun's BMH pseudo-order 4 where the needle is factorized. For some reason, GLIBC and musl use it, the latter all the way, whereas the former either BMH or Two-Way. Now, the AI and humans will be able to access this page/results and have something to improve upon.

Railgun_Trolldom has some hiccups (but they are easily fixable since they appear with 4..6 lengths), anyway, I stick to my guns.

Update from 2025-Mar-12:
The hiccups were due to the 15 years old code (the target was the Core 2) which fetched one DWORD at a time and using shifting - this proves slower on modern CPUs, I raised Railgun_Doublet's order to 4 thus tailoring Railgun_Tetralet which was much faster than the fastest Railgun_Quadruplet, however both Tetra and Quadro are brutally outperformed by my old Railgun_BMH2 (with 8KB table), as I said before the tweaking in old Railgun_Trolldom was a matter of shuffling IFs i.e. usecases to be handled properly by old functions of mine. I saw that latest 2.41 GLIBC uses some kind of BMH order 2 (with atavistic hashing) for needles up to 255, this is inferior to the more powerful full order 2, however when millions of hits occur this means that latency (the warm-up) is higher - I have to initialize bigger table, again, I stick to my guns as in the old time 15 years ago.

Corpus 'Gutenberg_html':
Compiler used: CLANG 19.1.7 (-O3 -mavx2)
Test machine: ThinkPad L490, 64GB DDR4, i7-8565U
Haystack: gutenberg_en_all_2023-08_132000-html-files.tar (~26 GiB)
Note: memmem_chatGPT uses BMH "bad-character" shift.
+-----------+-----+----------+--------------+--------------+----------------+----------------+--------------+--------------+----------------+
| Needle    | Len | Hits     | BMH_chatGPT  | GLIBC_memmem | Railgun_Scalar | Railgun_Vector | musl_memmem  | KMP_ChatGPT  | GLIBC/Trolldom |
+-----------+-----+----------+--------------+--------------+----------------+----------------+--------------+--------------+----------------+
| "fast"    | 4   | 861040   | 21874.127 ms | 7237.240 ms  | 7298.212 ms    | 2051.172 ms    | 13204.628 ms | 59593.656 ms | 0.99x          |
| "that"    | 4   | 39409672 | 23219.920 ms | 10142.697 ms | 13184.851 ms   | 4214.990 ms    | 13965.847 ms | 69010.485 ms | 0.77x          |
| "http"    | 4   | 1086637  | 19291.400 ms | 6710.708 ms  | 6924.697 ms    | 1941.519 ms    | 13738.335 ms | 65398.134 ms | 0.97x          |
| "mono"    | 4   | 178429   | 20743.550 ms | 7529.287 ms  | 7507.697 ms    | 1920.805 ms    | 13747.990 ms | 59726.073 ms | 1.00x          |
+-----------+-----+----------+--------------+--------------+----------------+----------------+--------------+--------------+----------------+
| "quick"   | 5   | 741907   | 15202.556 ms | 5081.373 ms  | 5282.580 ms    | 2039.986 ms    | 11478.977 ms | 57115.950 ms | 0.96x          |
| "sense"   | 5   | 898810   | 18863.728 ms | 5572.854 ms  | 5836.104 ms    | 2102.736 ms    | 20262.565 ms | 64691.890 ms | 0.95x          |
| "fifth"   | 5   | 108466   | 17616.947 ms | 7189.066 ms  | 6020.136 ms    | 1954.081 ms    | 18709.428 ms | 59659.332 ms | 1.19x          |
| "to an"   | 5   | 1093173  | 20318.401 ms | 8606.791 ms  | 7213.155 ms    | 2597.380 ms    | 35579.819 ms | 69429.318 ms | 1.19x          |
+-----------+-----+----------+--------------+--------------+----------------+----------------+--------------+--------------+----------------+
| "endure"  | 6   | 139716   | 16298.329 ms | 5957.643 ms  | 5281.980 ms    | 1915.754 ms    | 20444.352 ms | 72233.524 ms | 1.13x          |
| "London"  | 6   | 724854   | 15019.120 ms | 5601.943 ms  | 4863.998 ms    | 1981.511 ms    | 13286.475 ms | 57342.216 ms | 1.15x          |
| "strict"  | 6   | 560356   | 15085.979 ms | 5275.094 ms  | 5005.964 ms    | 2233.248 ms    | 18554.034 ms | 67324.607 ms | 1.05x          |
| "toward"  | 6   | 1306040  | 14388.026 ms | 5308.910 ms  | 5200.514 ms    | 2159.215 ms    | 20142.844 ms | 70040.596 ms | 1.02x          |
+-----------+-----+----------+--------------+--------------+----------------+----------------+--------------+--------------+----------------+
| "slowest" | 7   | 2152     | 13905.997 ms | 4491.385 ms  | 4149.382 ms    | 2021.872 ms    | 21261.204 ms | 67169.597 ms | 1.08x          |
| "Britain" | 7   | 163806   | 13899.570 ms | 5768.329 ms  | 4468.473 ms    | 1958.476 ms    | 18500.676 ms | 58071.149 ms | 1.29x          |
| "lioness" | 7   | 4715     | 13730.617 ms | 4880.518 ms  | 4519.353 ms    | 1956.757 ms    | 19711.737 ms | 62043.882 ms | 1.08x          |
| "to read" | 7   | 145683   | 13969.450 ms | 5302.419 ms  | 4794.759 ms    | 2229.952 ms    | 30013.817 ms | 70977.369 ms | 1.11x          |
+-----------+-----+----------+--------------+--------------+----------------+----------------+--------------+--------------+----------------+

Sanmayce:
How about Railgun_Scalar a.k.a. Railgun_Trolldom against musl_memmem?

Grok3:
4 Bytes

    "fast": 7298.212 ms vs. 13204.628 ms — 1.81x (Trolldom 1.81x faster).
    "that": 13184.851 ms vs. 13965.847 ms — 1.06x (Trolldom 1.06x faster).
    "http": 6924.697 ms vs. 13738.335 ms — 1.98x (Trolldom 1.98x faster).
    "mono": 7507.697 ms vs. 13747.990 ms — 1.83x (Trolldom 1.83x faster).
    Avg Ratio: 1.67x — Trolldom is ~1.67x faster.
    Hits Context: "that" (39.4M hits) tightens it; "http" (1.08M) shows Trolldom’s dominance.

5 Bytes

    "quick": 5282.580 ms vs. 11478.977 ms — 2.17x (Trolldom 2.17x faster).
    "sense": 5836.104 ms vs. 20262.565 ms — 3.47x (Trolldom 3.47x faster).
    "fifth": 6020.136 ms vs. 18709.428 ms — 3.11x (Trolldom 3.11x faster).
    "to an": 7213.155 ms vs. 35579.819 ms — 4.93x (Trolldom 4.93x faster).
    Avg Ratio: 3.42x — Trolldom is ~3.42x faster.
    Hits Context: "to an" (1.09M) amplifies Trolldom’s lead; "quick" (741K) still a blowout.

6 Bytes

    "endure": 5281.980 ms vs. 20444.352 ms — 3.87x (Trolldom 3.87x faster).
    "London": 4863.998 ms vs. 13286.475 ms — 2.73x (Trolldom 2.73x faster).
    "strict": 5005.964 ms vs. 18554.034 ms — 3.71x (Trolldom 3.71x faster).
    "toward": 5200.514 ms vs. 20142.844 ms — 3.87x (Trolldom 3.87x faster).
    Avg Ratio: 3.55x — Trolldom is ~3.55x faster.
    Hits Context: "toward" (1.3M) and "London" (724K) show consistent thrashing.

7 Bytes

    "slowest": 4149.382 ms vs. 21261.204 ms — 5.12x (Trolldom 5.12x faster).
    "Britain": 4468.473 ms vs. 18500.676 ms — 4.14x (Trolldom 4.14x faster).
    "lioness": 4519.353 ms vs. 19711.737 ms — 4.36x (Trolldom 4.36x faster).
    "to read": 4794.759 ms vs. 30013.817 ms — 6.26x (Trolldom 6.26x faster).
    Avg Ratio: 4.97x — Trolldom is ~4.97x faster.
    Hits Context: "to read" (145K) maximizes the gap; "slowest" (2K) still a massacre.

Summary

    Wins: Trolldom 16/16 — no contest, musl doesn’t win a single round.

Sanmayce:
How about Railgun_Scalar a.k.a. Railgun_Trolldom against GLIBC_memmem?

Grok3:
4 Bytes

    "fast": 7237.240 ms vs. 7298.212 ms — 0.99x (GLIBC slightly faster, 1.01x).
    "that": 10142.697 ms vs. 13184.851 ms — 0.77x (GLIBC faster, 1.30x).
    "http": 6710.708 ms vs. 6924.697 ms — 0.97x (GLIBC slightly faster, 1.03x).
    "mono": 7529.287 ms vs. 7507.697 ms — 1.00x (tie, even match).
    Avg Ratio: 0.93x — GLIBC is ~1.08x faster.
    Hits Context: "that" (39.4M hits) favors GLIBC’s Two-Way; "mono" (178K) shows Trolldom parity.

5 Bytes

    "quick": 5081.373 ms vs. 5282.580 ms — 0.96x (GLIBC slightly faster, 1.04x).
    "sense": 5572.854 ms vs. 5836.104 ms — 0.95x (GLIBC slightly faster, 1.05x).
    "fifth": 7189.066 ms vs. 6020.136 ms — 1.19x (Trolldom faster, 1.19x).
    "to an": 8606.791 ms vs. 7213.155 ms — 1.19x (Trolldom faster, 1.19x).
    Avg Ratio: 1.07x — Trolldom is ~1.07x faster.
    Hits Context: "to an" (1.09M) and "fifth" (108K) boost Trolldom; "quick" (741K) keeps GLIBC close.

6 Bytes

    "endure": 5957.643 ms vs. 5281.980 ms — 1.13x (Trolldom faster, 1.13x).
    "London": 5601.943 ms vs. 4863.998 ms — 1.15x (Trolldom faster, 1.15x).
    "strict": 5275.094 ms vs. 5005.964 ms — 1.05x (Trolldom faster, 1.05x).
    "toward": 5308.910 ms vs. 5200.514 ms — 1.02x (Trolldom faster, 1.02x).
    Avg Ratio: 1.09x — Trolldom is ~1.09x faster.
    Hits Context: "London" (724K) and "endure" (139K) highlight Trolldom’s edge; "toward" (1.3M) narrows it.

7 Bytes

    "slowest": 4491.385 ms vs. 4149.382 ms — 1.08x (Trolldom faster, 1.08x).
    "Britain": 5768.329 ms vs. 4468.473 ms — 1.29x (Trolldom faster, 1.29x).
    "lioness": 4880.518 ms vs. 4519.353 ms — 1.08x (Trolldom faster, 1.08x).
    "to read": 5302.419 ms vs. 4794.759 ms — 1.11x (Trolldom faster, 1.11x).
    Avg Ratio: 1.14x — Trolldom is ~1.14x faster.
    Hits Context: "Britain" (163K) maximizes Trolldom’s lead; "slowest" (2K) stays competitive.

Summary

    Wins: Trolldom 11/16 (most 5-7 bytes), GLIBC 3/16 ("that", "quick", "sense"), ties 2/16 ("fast", "mono").

Thus, since release 6 the hiccup was dealt with.

3d3178b2-70ad-43b4-96fb-c321d11f1a55

Oh, and the old place where the Railgun was crafted:
https://www.codeproject.com/Articles/250566/Fastest-strstr-like-function-in-C

Enfun!
2025-Mar-12, Sanmayce

String Matching Showdown against Text a short "benchmarking" review by Sanmayce, 2025-Apr-12

Two key methods were "enforced":

  • stressing RAM far outside the LLC (Last-Level-Cache)
  • stressing both Latency and Bandwidth of functions by finding all matches within the Haystack
  _________ __         .__                                    _____  __      _________.__                     .___                      
 /   _____//  |________|__| ____    ____   ________________ _/ ____\/  |_   /   _____/|  |__   ______  _  ____| _/______  _  ______     
 \_____  \\   __\_  __ \  |/    \  / ___\_/ ___\_  __ \__  \\   __\\   __\  \_____  \ |  |  \ /  _ \ \/ \/ / __ |/  _ \ \/ \/ /    \    
 /        \|  |  |  | \/  |   |  \/ /_/  >  \___|  | \// __ \|  |   |  |    /        \|   Y  (  <_> )     / /_/ (  <_> )     /   |  \   
/_______  /|__|  |__|  |__|___|  /\___  / \___  >__|  (____  /__|   |__|   /_______  /|___|  /\____/ \/\_/\____ |\____/ \/\_/|___|  /   
        \/                     \//_____/      \/           \/                      \/      \/                  \/                 \/    
________________    _______________________________ ____________________                                                    ___  ___    
\_   _____/  _  \  /   _____/\__    ___/\_   _____//   _____/\__    ___/   _____   ____   _____   _____   ____   _____     /  /  \  \   
 |    __)/  /_\  \ \_____  \   |    |    |    __)_ \_____  \   |    |     /     \_/ __ \ /     \ /     \_/ __ \ /     \   /  /    \  \  
 |     \/    |    \/        \  |    |    |        \/        \  |    |    |  Y Y  \  ___/|  Y Y  \  Y Y  \  ___/|  Y Y  \ (  (      )  ) 
 \___  /\____|__  /_______  /  |____|   /_______  /_______  /  |____|    |__|_|  /\___  >__|_|  /__|_|  /\___  >__|_|  /  \  \    /  /  
     \/         \/        \/                    \/        \/                   \/     \/      \/      \/     \/      \/    \__\  /__/   

In search for the FASTEST memmem()... finding needles in haystacks, FAST!

Tested datafile: gutenberg_en_all_2023-08_132000-html-files.tar, 26,914,119,680 bytes;

Tested machine: ThinkPad L490, i7-8565U "Whiskey Lake", TDP: 15 W (MAX 25 W), 14 nm, Max Turbo: 4,600 MHz, 2x32 GB DDR4 2400 MT/s, Linux Fedora 39;

Tested memmem-functions: BMH (order 1), GLIBC 2.38, Scalar and Vector Railguns, MUSL, KMP;

Tested compiler: CLANG 18.1.8, using -O3 -mavx2;

+------------------[ Length ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| Needle                      |        Hits |      BMH |    GLIBC | Trolldom | Nyotengu |     MUSL |   KMP | Speedup, Trolldom/GLIBC |
+-----------------[ 2 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "to"                        | 143,927,190 |    607.2 |   1460.8 |   1944.2 |  8012.5* |   1805.3 | 385.9 |   1944.2/1460.8 = 1.33x |
| ");"                        |     688,162 |    748.5 |   1543.4 |   2124.3 | 17107.6* |   1916.4 | 450.6 |   2124.3/1543.4 = 1.37x |
| "  "                        | 333,019,331 |    486.4 |   1412.5 |   1883.5 |  7159.1* |   1673.8 | 324.9 |   1883.5/1412.5 = 1.33x |
+-----------------[ 3 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "and"                       | 129,433,752 |    878.8 |   1697.0 |   2142.4 |  7767.2* |   1844.1 | 379.4 |   2142.4/1697.0 = 1.26x |
+-----------------[ 4 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "...""                      |   1,265,101 |   1382.7 |   3716.7 |   4924.3 | 12315.5* |   1781.0 | 430.8 |   4924.3/3716.7 = 1.32x |
| "fast"                      |     861,040 |   1172.5 |   3340.6 |   4293.6 | 12144.3* |   1837.1 | 424.3 |   4293.6/3340.6 = 1.29x |
| "that"                      |  39,409,672 |   1099.8 |   2381.6 |   3351.6 |  5653.3* |   1761.2 | 364.6 |   3351.6/2381.6 = 1.41x |
| "http"                      |   1,086,637 |   1277.6 |   3453.1 |   4720.6 | 12258.1* |   1750.1 | 401.9 |   4720.6/3453.1 = 1.37x |
| "mono"                      |     178,429 |   1237.3 |   3304.6 |   4295.9 | 12931.9* |   1834.1 | 423.0 |   4295.9/3304.6 = 1.30x |
| "mere"                      |     869,253 |   1064.3 |   2421.4 |   3414.4 | 12131.0* |   1817.0 | 414.8 |   3414.4/2421.4 = 1.41x |
| "buzz"                      |      33,352 |   1445.9 |   3730.8 |   5036.6 | 13058.7* |   1762.6 | 428.9 |   5036.6/3730.8 = 1.35x |
| "the "                      | 195,784,628 |    764.1 |   1331.7 |  2061.9* |   1779.4 |   1643.7 | 340.3 |   2061.9/1331.7 = 1.55x |
| "zest"                      |      14,976 |   1144.4 |   3435.7 |   4377.8 | 13207.4* |   1863.8 | 444.7 |   4377.8/3435.7 = 1.27x |
| "book"                      |   5,957,440 |   1367.5 |   3300.6 |   4656.7 | 10331.5* |   1805.9 | 417.8 |   4656.7/3300.6 = 1.41x |
| "xylo"                      |       2,278 |   1272.0 |   4102.0 |   5028.7 | 13234.3* |   1902.8 | 446.9 |   5028.7/4102.0 = 1.23x |
+-----------------[ 5 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "Jesus"                     |     290,706 |   1514.7 |   4620.5 |   4519.9 | 12964.1* |   1740.4 | 444.4 |   4519.9/4620.5 = 0.98x |
| "sense"                     |     898,810 |   1357.4 |   4355.3 |   4180.7 | 11835.5* |   1442.7 | 391.8 |   4180.7/4355.3 = 0.96x |
| "Satan"                     |      55,123 |   1422.9 |   3196.4 |   3966.1 | 13058.0* |   1473.8 | 437.9 |   3966.1/3196.4 = 1.24x |
| "Panza"                     |       2,612 |   1538.6 |   4257.1 |   4483.1 | 13271.3* |   2113.5 | 446.0 |   4483.1/4257.1 = 1.05x |
+-----------------[ 6 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "endure"                    |     139,716 |   1525.3 |   3921.4 |   4488.6 | 12442.2* |   1386.1 | 338.6 |   4488.6/3921.4 = 1.14x |
| "London"                    |     724,854 |   1705.8 |   4198.5 |   4868.6 | 12188.0* |   2160.8 | 440.0 |   4868.6/4198.5 = 1.16x |
| "strict"                    |     560,356 |   1618.2 |   4875.1 |   5038.0 | 11302.1* |   1577.5 | 383.7 |   5038.0/4875.1 = 1.03x |
| "toward"                    |   1,306,040 |   1785.3 |   4870.9 |   4926.2 | 11695.7* |   1422.3 | 376.9 |   4926.2/4870.9 = 1.01x |
| "thinks"                    |     180,426 |   1804.6 |   3942.7 |   4273.3 |  7905.2* |   1473.6 | 373.1 |   4273.3/3942.7 = 1.08x |
+-----------------[ 7 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "slowest"                   |       2,152 |   1845.2 |   5427.7 |   5960.1 | 12689.7* |   1348.6 | 399.0 |   5960.1/5427.7 = 1.10x |
| "Britain"                   |     163,806 |   1851.0 |   4369.0 |   5605.5 | 12720.1* |   1532.5 | 447.7 |   5605.5/4369.0 = 1.28x |
| "lioness"                   |       4,715 |   1867.9 |   4995.6 |   5453.8 | 12589.8* |   1371.5 | 405.8 |   5453.8/4995.6 = 1.09x |
| "to read"                   |     145,683 |   1838.0 |   4797.7 |   5276.7 | 11384.1* |    953.3 | 365.1 |   5276.7/4797.7 = 1.10x |
| "Quixote"                   |      19,820 |   1806.9 |   5961.4 |   6439.2 | 13313.3* |   1526.5 | 449.1 |   6439.2/5961.4 = 1.08x |
| "logical"                   |     315,326 |   1955.9 |   5231.3 |   6072.6 | 12086.8* |   1738.2 | 415.8 |   6072.6/5231.3 = 1.16x |
| "message"                   |     201,553 |   1836.1 |   4988.5 |   5658.5 | 12873.0* |   1695.2 | 420.9 |   5658.5/4988.5 = 1.13x |
| "sandals"                   |       9,031 |   2009.2 |   4628.1 |   5646.7 | 12427.7* |   1839.1 | 394.4 |   5646.7/4628.1 = 1.22x |
+----------------[ 13 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "Fleurs du mal"             |          15 |   2966.6 |   7364.1 |   9509.1 | 13721.3* |   1592.2 | 449.9 |   9509.1/7364.1 = 1.29x |
+----------------[ 14 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "Wilhelm Keitel"            |          26 |   3158.8 |   7795.8 |  11339.6 | 13030.3* |   1806.4 | 443.9 |  11339.6/7795.8 = 1.45x |
+----------------[ 23 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "Project Gutenberg Ebook"   |      75,733 |   4740.0 |   8920.4 |  11039.0 | 11954.9* |   2496.5 | 439.4 |  11039.0/8920.4 = 1.23x |
+----------------[ 25 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "Simplicius Simplicissimus" |           3 |   5472.9 |  10672.3 |  11841.0 | 12914.1* |   3625.9 | 443.0 | 11841.0/10672.3 = 1.10x |
+----------------[ 66 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "documen...ent to make you" |           3 |   5844.5 |   8593.4 | 19142.9* |  13010.6 |   3986.6 | 410.0 |  19142.9/8593.4 = 2.22x |
+----------------[ 71 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| "        <m... Ebooks." />" |      75,731 |   7355.1 |  11459.1 | 15861.3* |   4164.3 |   4999.6 | 301.2 | 15861.3/11459.1 = 1.38x |
+---------------[ 263 Bytes ]-+-------------+----------+----------+----------+----------+----------+-------+-------------------------+
| ""Choose a lan...y title"}" |      75,731 |   8464.9 |   6595.2 | 13975.5* |  12719.5 |   6180.9 | 428.2 |  13975.5/6595.2 = 2.11x |
+-----------------------------+-------------+----------+----------+----------+----------+----------+-------+-------------------------+

Note1: The numbers are in MiB/s, all results are obtained as superuser/root with maximum niceness -20;

Note2: 'BMH' and 'KMP' were written by chatGPT;

Note3: '*' denotes fastest result within the row;

Note4: Last needle (since it is > 256) is searched by Two-Way within the GLIBC, otherwise by pseudo-BMH order 2;

Note5: For range 8..12 long needles, Trolldom gets even faster because the my true BMH2 bitwise gains momentum;

Note6: The Trolldom's actual (for needles == 4) ASM code executed in this benchmark is this:

	cmp	ebx, 4
	jne	.LBB17_18
	mov	ecx, dword ptr [rsi]
	mov	edx, ecx
	shr	edx, 8
	mov	esi, ecx
	shr	esi, 16
	xor	r8d, r8d
	jmp	.LBB17_15
	.p2align	4, 0x90
.LBB17_13:
	lea	rax, [rdi + r8]
	mov	r9d, 1
	cmp	dword ptr [rax], ecx
	je	.LBB17_69
.LBB17_14:
	add	r8, r9
	cmp	r8, r12
	ja	.LBB17_1
.LBB17_15:
	movzx	eax, word ptr [rdi + r8 + 2]
	cmp	ax, si
	je	.LBB17_13
	cmp	ax, dx
	je	.LBB17_13
	mov	r9d, 3
	cmp	ax, cx
	jne	.LBB17_14
	jmp	.LBB17_13
.LBB17_18:
	cmp	ebx, 13 

Bottomline: Railgun 'Trolldom' is the fastest SCALAR hitter, with few hiccups/jams.

Enfun!
2025-Apr-12, Sanmayce

Add-on (2026-Jul-01): https://godbolt.org/z/z8fWvT88M

Add-on (2026-Jul-31):
Fastest FUZZY memmem() in C?!

The benchmark (fuzzorama.c) uses 25GB tarred 132000 html files from the Project Gutenberg to test few needles with different Levenshtein/edit distances.

Schatzhauser_logo

Railgun_Schatzhauser_AVX2 - The Fastest Fuzzy Finder for Levenshtein Distance <=2
Testmachine: i7-11850H (16 threads) 4x32GB DDR4, GCC v15.2.1
Tested in Performance Mode, with highest niceness

MULTI-THREADED SHOWDOWN (16 Cores Engaged):

+-------------+--------------+--------------------+---------------------------+---------------------+
| Needle Size | Distance (K) | WuManber64 (MiB/s) | Schatzhauser_AVX2 (MiB/s) | Speedup vs WuManber |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   7 bytes   |      2       |       2582.61      |          3296.59          |        1.28x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      1       |       3524.21      |         13027.93          |        3.70x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      2       |       2490.84      |          9428.57          |        3.79x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      3       |       1935.29      |          3254.69          |        1.68x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      4       |       1643.46      |           605.01          |        0.37x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   9 bytes   |      1       |       3752.75      |         14968.65          |        3.99x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   9 bytes   |      2       |       2604.42      |          6310.41          |        2.42x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   9 bytes   |      3       |       2036.61      |          1206.50          |        0.59x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
SINGLE-THREADED SHOWDOWN (1 Core Engaged):

+-------------+--------------+--------------------+---------------------------+---------------------+
| Needle Size | Distance (K) | WuManber64 (MiB/s) | Schatzhauser_AVX2 (MiB/s) | Speedup vs WuManber |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   7 bytes   |      2       |        429.00      |           731.31          |        1.70x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      1       |        673.42      |          2767.35          |        4.11x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      2       |        430.24      |          2214.01          |        5.15x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      3       |        386.31      |           676.07          |        1.75x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|  12 bytes   |      4       |        290.01      |           112.99          |        0.39x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   9 bytes   |      1       |        687.96      |          3421.57          |        4.97x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   9 bytes   |      2       |        424.89      |          1392.85          |        3.28x        |
+-------------+--------------+--------------------+---------------------------+---------------------+
|   9 bytes   |      3       |        357.32      |           234.93          |        0.66x        |
+-------------+--------------+--------------------+---------------------------+---------------------+

For K=2: Note1: Needle (7 bytes) "Quixote", yields 93,752 hits; Note2: Needle (12 bytes) "Schatzhauser", yields 46 hits; Note3: Needle (9 bytes) "Gutenberg", yields 3,322,534 hits;

https://godbolt.org/z/GdEzoTqd4

About

Fastest Scalar&Vector memmem() in C?!

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages