Benchmarks results from 2020-10-28 on Tal Einat's dev laptop. System details: Dell XPS 9560 Intel i7-7700HQ Ubuntu 20.04 Steps taken to prepare system for performance tuning: * Enable CPU isolation for two physical cores * Disable Intel p-state driver * Disable turbo-boost * Run `pyperf system tune` * Run benchmarks with CPU pinning random_bench.py results: /home/tal/dev/cpython $ taskset -c 2,3,6,7 ./pyperf-venv/bin/python ../random_bench.py --fast -o ../master-random.json needle=1: Mean +- std dev: 57.0 ns +- 0.7 ns needle=2: Mean +- std dev: 108 ns +- 22 ns needle=3: Mean +- std dev: 2.27 us +- 2.17 us needle=4: Mean +- std dev: 34.8 us +- 21.9 us needle=5: Mean +- std dev: 90.5 us +- 58.7 us needle=6: Mean +- std dev: 786 us +- 794 us needle=7: Mean +- std dev: 1.29 ms +- 1.07 ms needle=8: Mean +- std dev: 1.51 ms +- 0.25 ms needle=9: Mean +- std dev: 1.94 ms +- 0.18 ms needle=10: Mean +- std dev: 1.68 ms +- 0.35 ms needle=16: Mean +- std dev: 971 us +- 7 us needle=25: Mean +- std dev: 766 us +- 56 us needle=38: Mean +- std dev: 433 us +- 2 us needle=58: Mean +- std dev: 736 us +- 53 us needle=88: Mean +- std dev: 283 us +- 12 us needle=133: Mean +- std dev: 883 us +- 100 us needle=200: Mean +- std dev: 2.76 ms +- 2.13 ms needle=301: Mean +- std dev: 462 us +- 1 us needle=452: Mean +- std dev: 2.16 ms +- 0.06 ms needle=679: Mean +- std dev: 1.20 ms +- 0.73 ms needle=1019: Mean +- std dev: 3.40 ms +- 1.53 ms needle=1529: Mean +- std dev: 1.53 ms +- 0.02 ms needle=2294: Mean +- std dev: 1.51 ms +- 0.58 ms needle=3442: Mean +- std dev: 2.85 ms +- 1.14 ms needle=5164: Mean +- std dev: 685 us +- 165 us needle=7747: Mean +- std dev: 3.63 ms +- 1.81 ms needle=11621: Mean +- std dev: 4.07 ms +- 1.02 ms needle=17432: Mean +- std dev: 783 us +- 283 us needle=26149: Mean +- std dev: 2.77 ms +- 0.44 ms needle=39224: Mean +- std dev: 1.15 ms +- 0.64 ms needle=58837: Mean +- std dev: 985 us +- 87 us needle=88256: Mean +- std dev: 3.45 ms +- 1.80 ms /home/tal/dev/cpython-twoway $ taskset -c 2,3,6,7 ./pyperf-venv/bin/python ../random_bench.py --fast -o ../twoway-random.json needle=1: Mean +- std dev: 29.6 ns +- 1.1 ns needle=2: Mean +- std dev: 79.3 ns +- 23.5 ns needle=3: Mean +- std dev: 2.23 us +- 2.15 us needle=4: Mean +- std dev: 39.1 us +- 21.1 us needle=5: Mean +- std dev: 95.4 us +- 66.3 us needle=6: Mean +- std dev: 826 us +- 832 us needle=7: Mean +- std dev: 1.25 ms +- 1.04 ms needle=8: Mean +- std dev: 1.56 ms +- 0.21 ms needle=9: Mean +- std dev: 1.89 ms +- 0.17 ms needle=10: Mean +- std dev: 1.65 ms +- 0.31 ms needle=16: Mean +- std dev: 967 us +- 28 us needle=25: Mean +- std dev: 792 us +- 216 us needle=38: Mean +- std dev: 582 us +- 128 us needle=58: Mean +- std dev: 603 us +- 20 us needle=88: Mean +- std dev: 412 us +- 147 us needle=133: Mean +- std dev: 330 us +- 83 us needle=200: Mean +- std dev: 315 us +- 60 us needle=301: Mean +- std dev: 333 us +- 116 us needle=452: Mean +- std dev: 297 us +- 111 us needle=679: Mean +- std dev: 338 us +- 164 us needle=1019: Mean +- std dev: 196 us +- 16 us needle=1529: Mean +- std dev: 316 us +- 127 us needle=2294: Mean +- std dev: 470 us +- 18 us needle=3442: Mean +- std dev: 252 us +- 14 us needle=5164: Mean +- std dev: 291 us +- 64 us needle=7747: Mean +- std dev: 360 us +- 108 us needle=11621: Mean +- std dev: 381 us +- 16 us needle=17432: Mean +- std dev: 310 us +- 14 us needle=26149: Mean +- std dev: 583 us +- 1 us needle=39224: Mean +- std dev: 536 us +- 139 us needle=58837: Mean +- std dev: 670 us +- 136 us needle=88256: Mean +- std dev: 827 us +- 120 us /home/tal/dev $ pyperf compare_to master-random.json twoway-random.json -G Slower (2): - needle=88: 283 us +- 12 us -> 412 us +- 147 us: 1.45x slower (+45%) - needle=38: 433 us +- 2 us -> 582 us +- 128 us: 1.34x slower (+34%) Faster (20): - needle=1019: 3.40 ms +- 1.53 ms -> 196 us +- 16 us: 17.38x faster (-94%) - needle=3442: 2.85 ms +- 1.14 ms -> 252 us +- 14 us: 11.28x faster (-91%) - needle=11621: 4.07 ms +- 1.02 ms -> 381 us +- 16 us: 10.70x faster (-91%) - needle=7747: 3.63 ms +- 1.81 ms -> 360 us +- 108 us: 10.08x faster (-90%) - needle=200: 2.76 ms +- 2.13 ms -> 315 us +- 60 us: 8.76x faster (-89%) - needle=452: 2.16 ms +- 0.06 ms -> 297 us +- 111 us: 7.27x faster (-86%) - needle=1529: 1.53 ms +- 0.02 ms -> 316 us +- 127 us: 4.85x faster (-79%) - needle=26149: 2.77 ms +- 0.44 ms -> 583 us +- 1 us: 4.75x faster (-79%) - needle=88256: 3.45 ms +- 1.80 ms -> 827 us +- 120 us: 4.17x faster (-76%) - needle=679: 1.20 ms +- 0.73 ms -> 338 us +- 164 us: 3.54x faster (-72%) - needle=2294: 1.51 ms +- 0.58 ms -> 470 us +- 18 us: 3.21x faster (-69%) - needle=133: 883 us +- 100 us -> 330 us +- 83 us: 2.68x faster (-63%) - needle=17432: 783 us +- 283 us -> 310 us +- 14 us: 2.53x faster (-60%) - needle=5164: 685 us +- 165 us -> 291 us +- 64 us: 2.35x faster (-57%) - needle=39224: 1.15 ms +- 0.64 ms -> 536 us +- 139 us: 2.13x faster (-53%) - needle=1: 57.0 ns +- 0.7 ns -> 29.6 ns +- 1.1 ns: 1.93x faster (-48%) - needle=58837: 985 us +- 87 us -> 670 us +- 136 us: 1.47x faster (-32%) - needle=301: 462 us +- 1 us -> 333 us +- 116 us: 1.39x faster (-28%) - needle=2: 108 ns +- 22 ns -> 79.3 ns +- 23.5 ns: 1.37x faster (-27%) - needle=58: 736 us +- 53 us -> 603 us +- 20 us: 1.22x faster (-18%) stringbench.py results: /home/tal/dev/cpython $ taskset -c 2,3,6,7 ./python ./Tools/stringbench/stringbench.py stringbench v2.0 3.10.0a1+ (heads/master:fb5db7ec58, Oct 26 2020, 16:29:31) [GCC 9.3.0] 2020-10-28 09:41:02.794161 bytes unicode (in ms) (in ms) % comment ========== case conversion -- dense 0.45 0.48 93.0 ("WHERE IN THE WORLD IS CARMEN SAN DEIGO?"*10).lower() (*1000) 0.45 0.49 92.5 ("where in the world is carmen san deigo?"*10).upper() (*1000) ========== case conversion -- rare 0.45 0.48 93.9 ("Where in the world is Carmen San Deigo?"*10).lower() (*1000) 0.47 0.49 95.4 ("wHERE IN THE WORLD IS cARMEN sAN dEIGO?"*10).upper() (*1000) ========== concat 20 strings of words length 4 to 15 3.18 7.39 43.0 s1+s2+s3+s4+...+s20 (*1000) ========== concat two strings 0.18 0.23 76.2 "Andrew"+"Dalke" (*1000) ========== count AACT substrings in DNA example 0.69 0.70 98.7 dna.count("AACT") (*10) ========== count newlines 0.57 0.57 99.1 ...text.with.2000.newlines.count("\n") (*10) ========== early match, single character 0.21 0.21 100.1 ("A"*1000).find("A") (*1000) 0.96 0.06 1678.8 "A" in "A"*1000 (*1000) 0.21 0.21 98.5 ("A"*1000).index("A") (*1000) 0.43 2.05 20.8 ("A"*1000).partition("A") (*1000) 0.31 0.31 98.0 ("A"*1000).rfind("A") (*1000) 0.31 0.32 98.1 ("A"*1000).rindex("A") (*1000) 0.43 1.98 21.5 ("A"*1000).rpartition("A") (*1000) 0.54 2.14 25.0 ("A"*1000).rsplit("A", 1) (*1000) 0.54 2.18 25.0 ("A"*1000).split("A", 1) (*1000) ========== early match, two characters 0.21 0.21 98.5 ("AB"*1000).find("AB") (*1000) 0.96 0.06 1527.8 "AB" in "AB"*1000 (*1000) 0.21 0.22 97.0 ("AB"*1000).index("AB") (*1000) 0.53 3.54 14.8 ("AB"*1000).partition("AB") (*1000) 0.32 0.32 99.4 ("AB"*1000).rfind("AB") (*1000) 0.31 0.32 99.0 ("AB"*1000).rindex("AB") (*1000) 0.53 3.45 15.2 ("AB"*1000).rpartition("AB") (*1000) 0.64 3.68 17.5 ("AB"*1000).rsplit("AB", 1) (*1000) 0.64 3.64 17.6 ("AB"*1000).split("AB", 1) (*1000) ========== endswith multiple characters 0.19 0.20 94.4 "Andrew".endswith("Andrew") (*1000) ========== endswith multiple characters - not! 0.19 0.20 95.1 "Andrew".endswith("Anders") (*1000) ========== endswith single character 0.19 0.20 94.5 "Andrew".endswith("w") (*1000) ========== formatting a string type with a dict N/A 1.63 0.0 "The %(k1)s is %(k2)s the %(k3)s."%{"k1":"x","k2":"y","k3":"z",} (*1000) ========== join empty string, with 1 character sep N/A 0.04 0.0 "A".join("") (*100) ========== join empty string, with 5 character sep N/A 0.04 0.0 "ABCDE".join("") (*100) ========== join list of 100 words, with 1 character sep 2.39 4.36 54.9 "A".join(["Bob"]*100)) (*1000) ========== join list of 100 words, with 5 character sep 2.53 4.99 50.7 "ABCDE".join(["Bob"]*100)) (*1000) ========== join list of 26 characters, with 1 character sep 0.97 1.21 79.9 "A".join(list("ABC..Z")) (*1000) ========== join list of 26 characters, with 5 character sep 0.98 1.37 71.1 "ABCDE".join(list("ABC..Z")) (*1000) ========== join string with 26 characters, with 1 character sep N/A 2.29 0.0 "A".join("ABC..Z") (*1000) ========== join string with 26 characters, with 5 character sep N/A 2.42 0.0 "ABCDE".join("ABC..Z") (*1000) ========== late match, 100 characters 4.84 4.15 116.7 s="ABC"*33; ((s+"D")*500+s+"E").find(s+"E") (*100) 2.85 3.02 94.5 s="ABC"*33; ((s+"D")*500+"E"+s).find("E"+s) (*100) 2.44 2.47 98.9 s="ABC"*33; (s+"E") in ((s+"D")*300+s+"E") (*100) 4.82 4.15 116.3 s="ABC"*33; ((s+"D")*500+s+"E").index(s+"E") (*100) 3.61 11.01 32.8 s="ABC"*33; ((s+"D")*500+s+"E").partition(s+"E") (*100) 3.43 3.61 95.1 s="ABC"*33; ("E"+s+("D"+s)*500).rfind("E"+s) (*100) 1.99 3.01 66.1 s="ABC"*33; (s+"E"+("D"+s)*500).rfind(s+"E") (*100) 3.44 3.59 95.6 s="ABC"*33; ("E"+s+("D"+s)*500).rindex("E"+s) (*100) 4.03 11.32 35.6 s="ABC"*33; ("E"+s+("D"+s)*500).rpartition("E"+s) (*100) 4.50 11.44 39.3 s="ABC"*33; ("E"+s+("D"+s)*500).rsplit("E"+s, 1) (*100) 3.65 11.03 33.1 s="ABC"*33; ((s+"D")*500+s+"E").split(s+"E", 1) (*100) ========== late match, two characters 1.86 0.86 215.0 ("AB"*300+"C").find("BC") (*1000) 0.91 0.81 112.2 ("AB"*300+"CA").find("CA") (*1000) 1.40 0.50 278.7 "BC" in ("AB"*300+"C") (*1000) 1.85 0.87 213.7 ("AB"*300+"C").index("BC") (*1000) 1.59 1.83 87.2 ("AB"*300+"C").partition("BC") (*1000) 1.47 0.65 226.2 ("C"+"AB"*300).rfind("CA") (*1000) 0.77 0.87 88.4 ("BC"+"AB"*300).rfind("BC") (*1000) 1.47 0.65 224.7 ("C"+"AB"*300).rindex("CA") (*1000) 0.80 1.84 43.8 ("C"+"AB"*300).rpartition("CA") (*1000) 1.95 1.98 98.3 ("C"+"AB"*300).rsplit("CA", 1) (*1000) 1.83 2.74 67.0 ("AB"*300+"C").split("BC", 1) (*1000) ========== no match, single character 0.22 0.22 100.5 ("A"*1000).find("B") (*1000) 0.98 0.07 1404.1 "B" in "A"*1000 (*1000) 0.13 0.12 102.5 ("A"*1000).partition("B") (*1000) 0.22 0.22 99.6 ("A"*1000).rfind("B") (*1000) 0.13 0.12 106.2 ("A"*1000).rpartition("B") (*1000) 0.96 0.96 99.4 ("A"*1000).rsplit("B", 1) (*1000) 0.95 0.97 98.8 ("A"*1000).split("B", 1) (*1000) ========== no match, two characters 6.32 2.05 308.6 ("AB"*1000).find("BC") (*1000) 2.21 1.84 120.3 ("AB"*1000).find("CA") (*1000) 2.39 1.50 159.2 "BC" in "AB"*1000 (*1000) 5.14 1.55 332.6 ("AB"*1000).partition("BC") (*1000) 2.03 2.38 85.6 ("AB"*1000).rfind("BC") (*1000) 5.27 1.66 317.7 ("AB"*1000).rfind("CA") (*1000) 1.74 1.74 99.9 ("AB"*1000).rpartition("BC") (*1000) 2.20 2.05 107.5 ("AB"*1000).rsplit("BC", 1) (*1000) 5.75 5.12 112.4 ("AB"*1000).split("BC", 1) (*1000) ========== quick replace multiple character match 0.17 2.07 8.3 ("A" + ("Z"*128*1024)).replace("AZZ", "BBZZ", 1) (*10) ========== quick replace single character match 0.17 2.03 8.4 ("A" + ("Z"*128*1024)).replace("A", "BB", 1) (*10) ========== repeat 1 character 10 times 0.17 0.24 72.4 "A"*10 (*1000) ========== repeat 1 character 1000 times 0.32 1.80 17.9 "A"*1000 (*1000) ========== repeat 5 characters 10 times 0.19 0.32 58.4 "ABCDE"*10 (*1000) ========== repeat 5 characters 1000 times 0.71 7.87 9.1 "ABCDE"*1000 (*1000) ========== replace and expand multiple characters, big string 0.72 2.50 28.9 "...text.with.2000.newlines...replace("\n", "\r\n") (*10) ========== replace multiple characters, dna 1.16 2.53 45.7 dna.replace("ATC", "ATT") (*10) ========== replace single character 0.23 0.30 76.4 "This is a test".replace(" ", "\t") (*1000) ========== replace single character, big string 0.29 1.68 17.1 "...text.with.2000.lines...replace("\n", " ") (*10) ========== replace/remove multiple characters 0.28 0.39 71.2 "When shall we three meet again?".replace("ee", "") (*1000) ========== split 1 whitespace 0.34 0.48 70.9 ("Here are some words. "*2).partition(" ") (*1000) 0.24 0.36 68.0 ("Here are some words. "*2).rpartition(" ") (*1000) 0.44 0.60 74.4 ("Here are some words. "*2).rsplit(None, 1) (*1000) 0.44 0.59 73.9 ("Here are some words. "*2).split(None, 1) (*1000) ========== split 2000 newlines 3.37 5.70 59.1 "...text...".rsplit("\n") (*10) 3.27 5.60 58.4 "...text...".split("\n") (*10) 3.56 6.21 57.4 "...text...".splitlines() (*10) ========== split newlines 0.58 0.73 79.1 "this\nis\na\ntest\n".rsplit("\n") (*1000) 0.57 0.73 77.9 "this\nis\na\ntest\n".split("\n") (*1000) 0.56 0.70 79.2 "this\nis\na\ntest\n".splitlines() (*1000) ========== split on multicharacter separator (dna) 1.08 2.34 46.1 dna.rsplit("ACTAT") (*10) 1.13 2.43 46.4 dna.split("ACTAT") (*10) ========== split on multicharacter separator (small) 1.24 1.58 78.4 "this--is--a--test--of--the--emergency--broadcast--system".rsplit("--") (*1000) 1.24 1.60 77.6 "this--is--a--test--of--the--emergency--broadcast--system".split("--") (*1000) ========== split whitespace (huge) 3.80 5.19 73.1 human_text.rsplit() (*10) 3.74 5.21 71.8 human_text.split() (*10) ========== split whitespace (small) 1.12 1.51 73.6 ("Here are some words. "*2).rsplit() (*1000) 1.11 1.53 72.7 ("Here are some words. "*2).split() (*1000) ========== startswith multiple characters 0.19 0.21 94.1 "Andrew".startswith("Andrew") (*1000) ========== startswith multiple characters - not! 0.19 0.20 95.2 "Andrew".startswith("Anders") (*1000) ========== startswith single character 0.19 0.20 93.7 "Andrew".startswith("A") (*1000) ========== strip terminal newline 0.14 0.41 33.4 s="Hello!\n"; s[:-1] if s[-1]=="\n" else s (*1000) 0.16 0.22 73.0 "\nHello!".rstrip() (*1000) 0.16 0.22 72.5 "Hello!\n".rstrip() (*1000) 0.16 0.22 72.1 "\nHello!\n".strip() (*1000) 0.16 0.22 72.5 "\nHello!".strip() (*1000) 0.16 0.22 72.2 "Hello!\n".strip() (*1000) ========== strip terminal spaces and tabs 0.16 0.22 72.7 "\t \tHello".rstrip() (*1000) 0.16 0.22 71.9 "Hello\t \t".rstrip() (*1000) 0.05 0.07 74.7 "Hello\t \t".strip() (*1000) ========== tab split 0.90 1.27 71.1 GFF3_example.rsplit("\t", 8) (*1000) 0.89 1.25 71.1 GFF3_example.rsplit("\t") (*1000) 0.86 1.22 70.5 GFF3_example.split("\t", 8) (*1000) 0.89 1.26 71.0 GFF3_example.split("\t") (*1000) 151.95 232.12 65.5 TOTAL /home/tal/dev/cpython-twoway $ taskset -c 2,3,6,7 ./python ./Tools/stringbench/stringbench.py stringbench v2.0 3.10.0a1+ (heads/two-way-2:fe9e9d9c1f, Oct 26 2020, 11:12:48) [GCC 9.3.0] 2020-10-28 09:39:56.604042 bytes unicode (in ms) (in ms) % comment ========== case conversion -- dense 0.32 0.33 98.4 ("WHERE IN THE WORLD IS CARMEN SAN DEIGO?"*10).lower() (*1000) 0.47 0.47 98.7 ("where in the world is carmen san deigo?"*10).upper() (*1000) ========== case conversion -- rare 0.32 0.33 98.6 ("Where in the world is Carmen San Deigo?"*10).lower() (*1000) 0.47 0.48 98.6 ("wHERE IN THE WORLD IS cARMEN sAN dEIGO?"*10).upper() (*1000) ========== concat 20 strings of words length 4 to 15 0.97 0.87 111.6 s1+s2+s3+s4+...+s20 (*1000) ========== concat two strings 0.06 0.06 103.3 "Andrew"+"Dalke" (*1000) ========== count AACT substrings in DNA example 0.70 0.74 95.3 dna.count("AACT") (*10) ========== count newlines 0.97 0.65 150.2 ...text.with.2000.newlines.count("\n") (*10) ========== early match, single character 0.12 0.09 133.8 ("A"*1000).find("A") (*1000) 0.20 0.03 658.2 "A" in "A"*1000 (*1000) 0.12 0.12 102.9 ("A"*1000).index("A") (*1000) 0.13 0.17 77.6 ("A"*1000).partition("A") (*1000) 0.11 0.10 107.8 ("A"*1000).rfind("A") (*1000) 0.13 0.13 99.9 ("A"*1000).rindex("A") (*1000) 0.13 0.16 81.8 ("A"*1000).rpartition("A") (*1000) 0.14 0.19 73.8 ("A"*1000).rsplit("A", 1) (*1000) 0.14 0.19 73.1 ("A"*1000).split("A", 1) (*1000) ========== early match, two characters 0.10 0.09 108.8 ("AB"*1000).find("AB") (*1000) 0.19 0.03 556.8 "AB" in "AB"*1000 (*1000) 0.12 0.12 102.2 ("AB"*1000).index("AB") (*1000) 0.19 0.18 103.5 ("AB"*1000).partition("AB") (*1000) 0.11 0.11 105.3 ("AB"*1000).rfind("AB") (*1000) 0.13 0.13 99.7 ("AB"*1000).rindex("AB") (*1000) 0.19 0.18 106.3 ("AB"*1000).rpartition("AB") (*1000) 0.21 0.21 98.1 ("AB"*1000).rsplit("AB", 1) (*1000) 0.20 0.20 99.7 ("AB"*1000).split("AB", 1) (*1000) ========== endswith multiple characters 0.09 0.09 96.6 "Andrew".endswith("Andrew") (*1000) ========== endswith multiple characters - not! 0.09 0.09 102.6 "Andrew".endswith("Anders") (*1000) ========== endswith single character 0.09 0.09 96.5 "Andrew".endswith("w") (*1000) ========== formatting a string type with a dict N/A 0.44 0.0 "The %(k1)s is %(k2)s the %(k3)s."%{"k1":"x","k2":"y","k3":"z",} (*1000) ========== join empty string, with 1 character sep N/A 0.02 0.0 "A".join("") (*100) ========== join empty string, with 5 character sep N/A 0.02 0.0 "ABCDE".join("") (*100) ========== join list of 100 words, with 1 character sep 1.40 1.21 115.6 "A".join(["Bob"]*100)) (*1000) ========== join list of 100 words, with 5 character sep 1.45 1.34 108.7 "ABCDE".join(["Bob"]*100)) (*1000) ========== join list of 26 characters, with 1 character sep 0.45 0.36 125.5 "A".join(list("ABC..Z")) (*1000) ========== join list of 26 characters, with 5 character sep 0.45 0.38 120.4 "ABCDE".join(list("ABC..Z")) (*1000) ========== join string with 26 characters, with 1 character sep N/A 0.65 0.0 "A".join("ABC..Z") (*1000) ========== join string with 26 characters, with 5 character sep N/A 0.66 0.0 "ABCDE".join("ABC..Z") (*1000) ========== late match, 100 characters 7.19 7.23 99.5 s="ABC"*33; ((s+"D")*500+s+"E").find(s+"E") (*100) 0.09 0.08 103.0 s="ABC"*33; ((s+"D")*500+"E"+s).find("E"+s) (*100) 4.37 4.70 93.0 s="ABC"*33; (s+"E") in ((s+"D")*300+s+"E") (*100) 7.19 7.18 100.1 s="ABC"*33; ((s+"D")*500+s+"E").index(s+"E") (*100) 7.93 7.31 108.4 s="ABC"*33; ((s+"D")*500+s+"E").partition(s+"E") (*100) 3.89 3.90 99.8 s="ABC"*33; ("E"+s+("D"+s)*500).rfind("E"+s) (*100) 3.03 2.76 109.7 s="ABC"*33; (s+"E"+("D"+s)*500).rfind(s+"E") (*100) 5.66 5.06 111.8 s="ABC"*33; ("E"+s+("D"+s)*500).rindex("E"+s) (*100) 3.99 4.03 99.2 s="ABC"*33; ("E"+s+("D"+s)*500).rpartition("E"+s) (*100) 4.00 4.05 99.0 s="ABC"*33; ("E"+s+("D"+s)*500).rsplit("E"+s, 1) (*100) 7.94 7.32 108.4 s="ABC"*33; ((s+"D")*500+s+"E").split(s+"E", 1) (*100) ========== late match, two characters 0.47 0.59 80.0 ("AB"*300+"C").find("BC") (*1000) 0.63 0.79 80.1 ("AB"*300+"CA").find("CA") (*1000) 0.69 0.52 130.9 "BC" in ("AB"*300+"C") (*1000) 0.47 0.46 100.9 ("AB"*300+"C").index("BC") (*1000) 0.89 0.45 195.5 ("AB"*300+"C").partition("BC") (*1000) 0.71 0.64 110.5 ("C"+"AB"*300).rfind("CA") (*1000) 0.92 0.70 132.7 ("BC"+"AB"*300).rfind("BC") (*1000) 0.82 2.54 32.5 ("C"+"AB"*300).rindex("CA") (*1000) 0.77 0.59 130.7 ("C"+"AB"*300).rpartition("CA") (*1000) 0.77 0.69 110.9 ("C"+"AB"*300).rsplit("CA", 1) (*1000) 0.90 0.58 155.0 ("AB"*300+"C").split("BC", 1) (*1000) ========== no match, single character 0.11 0.10 112.0 ("A"*1000).find("B") (*1000) 0.21 0.04 539.3 "B" in "A"*1000 (*1000) 0.10 0.09 118.6 ("A"*1000).partition("B") (*1000) 0.12 0.11 110.7 ("A"*1000).rfind("B") (*1000) 0.11 0.08 126.0 ("A"*1000).rpartition("B") (*1000) 0.47 0.47 100.1 ("A"*1000).rsplit("B", 1) (*1000) 0.82 0.46 177.4 ("A"*1000).split("B", 1) (*1000) ========== no match, two characters 1.28 1.70 74.9 ("AB"*1000).find("BC") (*1000) 1.78 2.34 76.0 ("AB"*1000).find("CA") (*1000) 1.81 1.65 109.8 "BC" in "AB"*1000 (*1000) 2.60 1.16 224.1 ("AB"*1000).partition("BC") (*1000) 2.83 2.02 140.2 ("AB"*1000).rfind("BC") (*1000) 1.92 1.89 101.6 ("AB"*1000).rfind("CA") (*1000) 2.57 2.27 113.2 ("AB"*1000).rpartition("BC") (*1000) 2.68 2.22 120.7 ("AB"*1000).rsplit("BC", 1) (*1000) 2.61 1.53 170.4 ("AB"*1000).split("BC", 1) (*1000) ========== quick replace multiple character match 0.05 0.04 103.3 ("A" + ("Z"*128*1024)).replace("AZZ", "BBZZ", 1) (*10) ========== quick replace single character match 0.05 0.04 103.3 ("A" + ("Z"*128*1024)).replace("A", "BB", 1) (*10) ========== repeat 1 character 10 times 0.06 0.06 92.6 "A"*10 (*1000) ========== repeat 1 character 1000 times 0.12 0.12 96.3 "A"*1000 (*1000) ========== repeat 5 characters 10 times 0.08 0.08 95.2 "ABCDE"*10 (*1000) ========== repeat 5 characters 1000 times 0.22 0.20 107.3 "ABCDE"*1000 (*1000) ========== replace and expand multiple characters, big string 0.68 1.14 59.9 "...text.with.2000.newlines...replace("\n", "\r\n") (*10) ========== replace multiple characters, dna 1.29 1.09 118.4 dna.replace("ATC", "ATT") (*10) ========== replace single character 0.10 0.09 116.9 "This is a test".replace(" ", "\t") (*1000) ========== replace single character, big string 0.22 0.41 53.9 "...text.with.2000.lines...replace("\n", " ") (*10) ========== replace/remove multiple characters 0.15 0.13 110.8 "When shall we three meet again?".replace("ee", "") (*1000) ========== split 1 whitespace 0.13 0.11 111.8 ("Here are some words. "*2).partition(" ") (*1000) 0.11 0.10 115.7 ("Here are some words. "*2).rpartition(" ") (*1000) 0.13 0.14 92.8 ("Here are some words. "*2).rsplit(None, 1) (*1000) 0.12 0.13 91.4 ("Here are some words. "*2).split(None, 1) (*1000) ========== split 2000 newlines 1.21 1.22 99.5 "...text...".rsplit("\n") (*10) 1.23 1.15 107.3 "...text...".split("\n") (*10) 1.45 1.50 96.9 "...text...".splitlines() (*10) ========== split newlines 0.16 0.17 96.8 "this\nis\na\ntest\n".rsplit("\n") (*1000) 0.15 0.16 95.9 "this\nis\na\ntest\n".split("\n") (*1000) 0.14 0.15 90.9 "this\nis\na\ntest\n".splitlines() (*1000) ========== split on multicharacter separator (dna) 0.98 0.89 110.2 dna.rsplit("ACTAT") (*10) 1.07 0.88 121.7 dna.split("ACTAT") (*10) ========== split on multicharacter separator (small) 0.33 0.35 94.7 "this--is--a--test--of--the--emergency--broadcast--system".rsplit("--") (*1000) 0.32 0.31 103.9 "this--is--a--test--of--the--emergency--broadcast--system".split("--") (*1000) ========== split whitespace (huge) 0.82 0.92 89.1 human_text.rsplit() (*10) 0.81 0.89 90.6 human_text.split() (*10) ========== split whitespace (small) 0.25 0.29 87.9 ("Here are some words. "*2).rsplit() (*1000) 0.25 0.28 90.2 ("Here are some words. "*2).split() (*1000) ========== startswith multiple characters 0.12 0.09 128.2 "Andrew".startswith("Andrew") (*1000) ========== startswith multiple characters - not! 0.12 0.09 133.7 "Andrew".startswith("Anders") (*1000) ========== startswith single character 0.12 0.09 128.5 "Andrew".startswith("A") (*1000) ========== strip terminal newline 0.06 0.12 51.3 s="Hello!\n"; s[:-1] if s[-1]=="\n" else s (*1000) 0.05 0.05 98.0 "\nHello!".rstrip() (*1000) 0.05 0.05 96.6 "Hello!\n".rstrip() (*1000) 0.05 0.05 98.6 "\nHello!\n".strip() (*1000) 0.05 0.05 97.2 "\nHello!".strip() (*1000) 0.05 0.05 98.8 "Hello!\n".strip() (*1000) ========== strip terminal spaces and tabs 0.05 0.05 100.2 "\t \tHello".rstrip() (*1000) 0.05 0.05 96.5 "Hello\t \t".rstrip() (*1000) 0.03 0.03 103.8 "Hello\t \t".strip() (*1000) ========== tab split 0.26 0.27 98.2 GFF3_example.rsplit("\t", 8) (*1000) 0.26 0.26 99.8 GFF3_example.rsplit("\t") (*1000) 0.23 0.23 100.5 GFF3_example.split("\t", 8) (*1000) 0.27 0.25 108.7 GFF3_example.split("\t") (*1000) 110.80 106.82 103.7 TOTAL