Skip to content

Conversation

bboreham
Copy link
Contributor

@bboreham bboreham commented Oct 22, 2024

This is cherry-picking prometheus/prometheus#15210 and prometheus/prometheus#15237.

Do a better job of regexps with a long series of choices followed by .*, like:

		"(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuSoAl.*|qbvKMimS.*|IecrXtPa.*|seTckYqt.*|NxnyHkgB.*|fIDlOgKb.*|UhlWIygH.*|OtNoJxHG.*|cUTkFVIV.*|mTgFIHjr.*|jQkoIDtE.*|PPMKxRXl.*|AwMfwVkQ.*|CQyMrTQJ.*|BzrqxVSi.*|nTpcWuhF.*|PertdywG.*|ZZDgCtXN.*|WWdDPyyE.*|uVtNQsKk.*|BdeCHvPZ.*|wshRnFlH.*|aOUIitIp.*|RxZeCdXT.*|CFZMslCj.*|AVBZRDxl.*|IzIGCnhw.*|ythYuWiz.*|oztXVXhl.*|VbLkwqQx.*|qvaUgyVC.*|VawUjPWC.*|ecloYJuj.*|boCLTdSU.*|uPrKeAZx.*|hrMWLWBq.*|JOnUNHRM.*|rYnujkPq.*|dDEdZhIj.*|DRrfvugG.*|yEGfDxVV.*|YMYdJWuP.*|PHUQZNWM.*|AmKNrLis.*|zTxndVfn.*|FPsHoJnc.*|EIulZTua.*|KlAPhdzg.*|ScHJJCLt.*|NtTfMzME.*|eMCwuFdo.*|SEpJVJbR.*|cdhXZeCx.*|sAVtBwRh.*|kVFEVcMI.*|jzJrxraA.*|tGLHTell.*|NNWoeSaw.*|DcOKSetX.*|UXZAJyka.*|THpMphDP.*|rizheevl.*|kDCBRidd.*|pCZZRqyu.*|pSygkitl.*|SwZGkAaW.*|wILOrfNX.*|QkwVOerj.*|kHOMxPDr.*|EwOVycJv.*|AJvtzQFS.*|yEOjKYYB.*|LizIINLL.*|JBRSsfcG.*|YPiUqqNl.*|IsdEbvee.*|MjEpGcBm.*|OxXZVgEQ.*|xClXGuxa.*|UzRCGFEb.*|buJbvfvA.*|IPZQxRet.*|oFYShsMc.*|oBHffuHO.*|bzzKrcBR.*|KAjzrGCl.*|IPUsAVls.*|OGMUMbIU.*|gyDccHuR.*|bjlalnDd.*|ZLWjeMna.*|fdsuIlxQ.*|dVXtiomV.*|XxedTjNg.*|XWMHlNoA.*|nnyqArQX.*|opfkWGhb.*|wYtnhdYb.*))",

Benchmarks:

goos: linux
goarch: amd64
pkg: github.com/prometheus/prometheus/model/labels
cpu: Intel(R) Core(TM) i7-14700K
                                                             │  before.txt   │              after.txt              │
                                                             │    sec/op     │    sec/op     vs base               │
FastRegexMatcher/#00-28                                         30.64n ±  1%   30.54n ±  0%   -0.31% (p=0.013 n=6)
FastRegexMatcher/()-28                                          30.53n ±  1%   30.54n ±  0%        ~ (p=0.797 n=6)
FastRegexMatcher/foo-28                                         34.98n ±  2%   35.49n ±  4%        ~ (p=0.132 n=6)
FastRegexMatcher/foo()-28                                       32.72n ±  2%   32.48n ±  1%        ~ (p=0.132 n=6)
FastRegexMatcher/^foo-28                                        32.64n ±  1%   32.46n ±  1%        ~ (p=0.310 n=6)
FastRegexMatcher/(foo|bar)-28                                   40.03n ±  2%   39.81n ±  1%        ~ (p=0.240 n=6)
FastRegexMatcher/foo.*-28                                       88.04n ±  4%   88.31n ±  3%        ~ (p=0.818 n=6)
FastRegexMatcher/.*foo-28                                       92.68n ±  2%   93.71n ±  3%        ~ (p=0.394 n=6)
FastRegexMatcher/^.*foo$-28                                     92.52n ±  2%   91.69n ±  3%        ~ (p=0.394 n=6)
FastRegexMatcher/^.+foo$-28                                     92.53n ±  2%   93.77n ±  4%        ~ (p=0.394 n=6)
FastRegexMatcher/.?-28                                          48.95n ±  3%   45.94n ±  1%   -6.15% (p=0.002 n=6)
FastRegexMatcher/.*-28                                          56.60n ±  1%   56.67n ±  0%        ~ (p=0.818 n=6)
FastRegexMatcher/().*-28                                        56.60n ±  3%   56.65n ±  1%        ~ (p=0.699 n=6)
FastRegexMatcher/.*()-28                                        56.43n ±  1%   56.74n ±  4%        ~ (p=0.071 n=6)
FastRegexMatcher/().*()-28                                      56.44n ±  0%   56.75n ±  0%   +0.54% (p=0.009 n=6)
FastRegexMatcher/.+-28                                          64.14n ±  1%   58.23n ±  0%   -9.22% (p=0.002 n=6)
FastRegexMatcher/.+()-28                                        63.91n ±  1%   58.34n ±  1%   -8.72% (p=0.002 n=6)
FastRegexMatcher/foo.+-28                                       89.91n ±  7%   87.30n ±  5%        ~ (p=0.132 n=6)
FastRegexMatcher/.+foo-28                                       92.22n ±  3%   91.93n ±  3%        ~ (p=0.937 n=6)
FastRegexMatcher/foo_.+-28                                      79.14n ±  9%   79.27n ±  5%        ~ (p=0.589 n=6)
FastRegexMatcher/foo_.*-28                                      77.31n ± 10%   80.60n ±  4%        ~ (p=0.180 n=6)
FastRegexMatcher/.*foo.*-28                                     143.8n ±  0%   144.4n ±  0%   +0.42% (p=0.011 n=6)
FastRegexMatcher/.+foo.+-28                                     149.6n ±  1%   148.5n ±  0%   -0.74% (p=0.004 n=6)
FastRegexMatcher/(?s:.*)-28                                     30.52n ±  0%   30.57n ±  0%        ~ (p=0.121 n=6)
FastRegexMatcher/(?s:.+)-28                                     33.80n ±  2%   33.80n ±  0%        ~ (p=1.000 n=6)
FastRegexMatcher/(?s:^.*foo$)-28                                91.40n ±  3%   92.20n ±  4%        ~ (p=0.699 n=6)
FastRegexMatcher/(?i:foo)-28                                    59.48n ± 11%   58.60n ±  1%   -1.48% (p=0.002 n=6)
FastRegexMatcher/(?i:(foo|bar))-28                              136.3n ±  0%   133.5n ±  0%   -2.09% (p=0.002 n=6)
FastRegexMatcher/(?i:(foo1|foo2|bar))-28                        222.1n ±  1%   223.2n ±  1%   +0.47% (p=0.045 n=6)
FastRegexMatcher/^(?i:foo|oo)|(bar)$-28                         507.7n ±  1%   509.7n ±  1%        ~ (p=0.310 n=6)
FastRegexMatcher/(?i:(foo1|foo2|aaa|bbb|ccc|ddd|e-28            687.1n ±  1%   526.0n ±  0%  -23.44% (p=0.002 n=6)
FastRegexMatcher/((.*)(bar|b|buzz)(.+)|foo)$-28                 345.2n ±  0%   344.1n ±  0%   -0.33% (p=0.002 n=6)
FastRegexMatcher/^$-28                                          30.58n ±  0%   30.54n ±  1%        ~ (p=0.513 n=6)
FastRegexMatcher/(prometheus|api_prom)_api_v1_.+-28             111.3n ±  0%   110.6n ±  0%   -0.58% (p=0.002 n=6)
FastRegexMatcher/10\.0\.(1|2)\.+-28                             80.71n ±  9%   80.34n ±  6%        ~ (p=0.623 n=6)
FastRegexMatcher/10\.0\.(1|2).+-28                              81.55n ±  9%   82.96n ± 10%        ~ (p=0.937 n=6)
FastRegexMatcher/((fo(bar))|.+foo)-28                           183.3n ±  1%   186.5n ±  0%   +1.75% (p=0.002 n=6)
FastRegexMatcher/zQPbMkNO|NNSPdvMi|iWuuSoAl|qbvKM-28            183.1n ±  1%   178.6n ±  1%   -2.48% (p=0.002 n=6)
FastRegexMatcher/jyyfj00j0061|jyyfj00j0062|jyyfj9-28            168.3n ±  2%   181.0n ±  1%   +7.54% (p=0.002 n=6)
FastRegexMatcher/(?i:(zQPbMkNO|NNSPdvMi|iWuuSoAl|-28            694.3n ±  2%   527.4n ±  0%  -24.05% (p=0.002 n=6)
FastRegexMatcher/(?i:(AAAAAAAAAAAAAAAAAAAAAAAA|BB-28            212.9n ±  0%   211.2n ±  1%   -0.82% (p=0.002 n=6)
FastRegexMatcher/(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuS-28            171.8n ±  1%   172.2n ±  1%        ~ (p=0.199 n=6)
FastRegexMatcher/(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuS#01-28         839.6n ±  1%   299.9n ±  0%  -64.28% (p=0.002 n=6)
FastRegexMatcher/(?i:(.*zQPbMkNO|.*NNSPdvMi|.*iWu-28            5.158µ ±  0%   5.150µ ±  0%        ~ (p=0.065 n=6)
FastRegexMatcher/fo.?-28                                        89.38n ±  1%   85.39n ±  9%        ~ (p=0.310 n=6)
FastRegexMatcher/foo.?-28                                       89.31n ±  4%   89.84n ±  2%        ~ (p=0.589 n=6)
FastRegexMatcher/f.?o-28                                        90.08n ±  4%   84.83n ±  7%   -5.83% (p=0.009 n=6)
FastRegexMatcher/.*foo.?-28                                     149.4n ±  1%   148.8n ±  1%        ~ (p=0.104 n=6)
FastRegexMatcher/.?foo.+-28                                     144.0n ±  1%   143.0n ±  0%   -0.69% (p=0.006 n=6)
FastRegexMatcher/foo.?|bar-28                                   138.6n ±  4%   139.2n ±  3%        ~ (p=0.937 n=6)
FastRegexMatcher/ſſs-28                                         34.96n ±  2%   35.95n ±  1%   +2.83% (p=0.004 n=6)
FastRegexMatcher/.*-.*-.*-.*-.*-28                              130.4n ±  1%   126.1n ±  1%   -3.33% (p=0.002 n=6)
FastRegexMatcher/(.+)-(.+)-(.+)-(.+)-(.+)-28                    130.3n ±  1%   125.7n ±  1%   -3.57% (p=0.002 n=6)
FastRegexMatcher/((.*))(?i:f)((.*))o((.*))o((.*))-28            2.751µ ±  0%   2.760µ ±  0%   +0.33% (p=0.009 n=6)
FastRegexMatcher/((.*))f((.*))(?i:o)((.*))o((.*))-28            2.190µ ±  0%   2.201µ ±  0%   +0.53% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=none/ascii=true-28        4.152n ±  0%   4.653n ±  1%  +12.07% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=none/ascii=false-28       111.5n ±  0%   113.4n ±  0%   +1.75% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=first/ascii=true-28      17.150n ±  1%   7.856n ±  1%  -54.20% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=first/ascii=false-28      135.4n ±  0%   129.4n ±  1%   -4.43% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=last/ascii=true-28       16.995n ±  1%   7.833n ±  0%  -53.91% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=last/ascii=false-28       125.7n ±  0%   126.7n ±  2%   +0.84% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=all/ascii=true-28        18.875n ±  1%   9.577n ±  0%  -49.26% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=all/ascii=false-28        124.5n ±  0%   120.0n ±  1%   -3.61% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=none/ascii=true-28       32.64n ±  2%   38.75n ±  0%  +18.70% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=none/ascii=false-28      1.734µ ±  2%   1.728µ ±  1%        ~ (p=0.169 n=6)
ToNormalizedLower/length=100/uppercase=first/ascii=true-28      61.87n ±  1%   42.42n ±  1%  -31.44% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=first/ascii=false-28     1.805µ ±  0%   1.794µ ±  1%        ~ (p=0.104 n=6)
ToNormalizedLower/length=100/uppercase=last/ascii=true-28       63.37n ±  0%   40.45n ±  0%  -36.16% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=last/ascii=false-28      1.745µ ±  1%   1.735µ ±  0%   -0.54% (p=0.037 n=6)
ToNormalizedLower/length=100/uppercase=all/ascii=true-28        81.14n ±  1%   61.95n ±  1%  -23.65% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=all/ascii=false-28       1.683µ ±  1%   1.669µ ±  1%   -0.86% (p=0.006 n=6)
ToNormalizedLower/length=1000/uppercase=none/ascii=true-28      285.8n ±  1%   340.6n ±  1%  +19.17% (p=0.002 n=6)
ToNormalizedLower/length=1000/uppercase=none/ascii=false-28     15.13µ ±  0%   15.12µ ±  0%        ~ (p=0.699 n=6)
ToNormalizedLower/length=1000/uppercase=first/ascii=true-28     435.6n ±  5%   524.8n ±  1%  +20.46% (p=0.002 n=6)
ToNormalizedLower/length=1000/uppercase=first/ascii=false-28    15.59µ ±  0%   15.55µ ±  1%        ~ (p=0.485 n=6)
ToNormalizedLower/length=1000/uppercase=last/ascii=true-28      433.1n ±  1%   436.4n ±  1%        ~ (p=0.065 n=6)
ToNormalizedLower/length=1000/uppercase=last/ascii=false-28     15.08µ ±  0%   15.07µ ±  1%        ~ (p=0.909 n=6)
ToNormalizedLower/length=1000/uppercase=all/ascii=true-28       603.4n ±  1%   672.9n ±  1%  +11.50% (p=0.002 n=6)
ToNormalizedLower/length=1000/uppercase=all/ascii=false-28      14.43µ ±  0%   14.38µ ±  0%   -0.36% (p=0.002 n=6)
ToNormalizedLower/length=4000/uppercase=none/ascii=true-28      1.111µ ±  0%   1.305µ ±  2%  +17.47% (p=0.002 n=6)
ToNormalizedLower/length=4000/uppercase=none/ascii=false-28     58.91µ ±  6%   58.68µ ±  0%   -0.39% (p=0.002 n=6)
ToNormalizedLower/length=4000/uppercase=first/ascii=true-28     1.642µ ±  2%   1.991µ ±  1%  +21.29% (p=0.002 n=6)
ToNormalizedLower/length=4000/uppercase=first/ascii=false-28    60.57µ ±  1%   60.78µ ±  0%        ~ (p=0.149 n=6)
ToNormalizedLower/length=4000/uppercase=last/ascii=true-28      1.656µ ±  4%   1.810µ ±  2%   +9.33% (p=0.002 n=6)
ToNormalizedLower/length=4000/uppercase=last/ascii=false-28     58.72µ ±  2%   58.70µ ±  4%        ~ (p=0.974 n=6)
ToNormalizedLower/length=4000/uppercase=all/ascii=true-28       2.513µ ±  1%   2.678µ ±  4%   +6.59% (p=0.002 n=6)
ToNormalizedLower/length=4000/uppercase=all/ascii=false-28      56.58µ ±  0%   56.47µ ±  0%        ~ (p=0.240 n=6)
ZeroOrOneCharacterStringMatcher-28                              2.436n ±  0%   2.434n ±  1%        ~ (p=0.515 n=6)
geomean                                                         219.0n         209.1n         -4.49%

                                                             │   before.txt   │                after.txt                 │
                                                             │      B/op      │     B/op      vs base                    │
FastRegexMatcher/#00-28                                          0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/()-28                                           0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo-28                                          0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo()-28                                        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^foo-28                                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(foo|bar)-28                                    0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.*-28                                        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*foo-28                                        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^.*foo$-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^.+foo$-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.?-28                                           0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*-28                                           0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/().*-28                                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*()-28                                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/().*()-28                                       0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+-28                                           0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+()-28                                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.+-28                                        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+foo-28                                        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo_.+-28                                       0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo_.*-28                                       0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*foo.*-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+foo.+-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?s:.*)-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?s:.+)-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?s:^.*foo$)-28                                 0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:foo)-28                                     0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(foo|bar))-28                               0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(foo1|foo2|bar))-28                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^(?i:foo|oo)|(bar)$-28                          0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(foo1|foo2|aaa|bbb|ccc|ddd|e-28             560.0 ± 0%       240.0 ± 0%   -57.14% (p=0.002 n=6)
FastRegexMatcher/((.*)(bar|b|buzz)(.+)|foo)$-28                  0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^$-28                                           0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(prometheus|api_prom)_api_v1_.+-28              0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/10\.0\.(1|2)\.+-28                              0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/10\.0\.(1|2).+-28                               0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/((fo(bar))|.+foo)-28                            0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/zQPbMkNO|NNSPdvMi|iWuuSoAl|qbvKM-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/jyyfj00j0061|jyyfj00j0062|jyyfj9-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(zQPbMkNO|NNSPdvMi|iWuuSoAl|-28             560.0 ± 0%       240.0 ± 0%   -57.14% (p=0.002 n=6)
FastRegexMatcher/(?i:(AAAAAAAAAAAAAAAAAAAAAAAA|BB-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuS-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuS#01-28          560.0 ± 0%         0.0 ± 0%  -100.00% (p=0.002 n=6)
FastRegexMatcher/(?i:(.*zQPbMkNO|.*NNSPdvMi|.*iWu-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/fo.?-28                                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.?-28                                        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/f.?o-28                                         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*foo.?-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.?foo.+-28                                      0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.?|bar-28                                    0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/ſſs-28                                          0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*-.*-.*-.*-.*-28                               0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(.+)-(.+)-(.+)-(.+)-(.+)-28                     0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/((.*))(?i:f)((.*))o((.*))o((.*))-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/((.*))f((.*))(?i:o)((.*))o((.*))-28             0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=none/ascii=true-28         0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=none/ascii=false-28        9.000 ± 0%       9.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=first/ascii=true-28        16.00 ± 0%        0.00 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=first/ascii=false-28       17.00 ± 0%       11.00 ± 0%   -35.29% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=last/ascii=true-28         16.00 ± 0%        0.00 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=last/ascii=false-28        11.00 ± 0%       11.00 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=all/ascii=true-28          16.00 ± 0%        0.00 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=all/ascii=false-28         22.00 ± 0%       16.00 ± 0%   -27.27% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=none/ascii=true-28        0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=none/ascii=false-28     1.050Ki ± 0%     1.050Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=first/ascii=true-28       112.0 ± 0%         0.0 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=first/ascii=false-28    1.104Ki ± 0%     1.061Ki ± 0%    -3.98% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=last/ascii=true-28        112.0 ± 0%         0.0 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=last/ascii=false-28     1.061Ki ± 0%     1.061Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=all/ascii=true-28         112.0 ± 0%         0.0 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=all/ascii=false-28      1.137Ki ± 0%     1.094Ki ± 0%    -3.78% (p=0.002 n=6)
ToNormalizedLower/length=1000/uppercase=none/ascii=true-28       0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=none/ascii=false-28    4.862Ki ± 0%     4.862Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=first/ascii=true-28    1.000Ki ± 0%     1.000Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=first/ascii=false-28   5.375Ki ± 0%     5.375Ki ± 0%         ~ (p=1.000 n=6)
ToNormalizedLower/length=1000/uppercase=last/ascii=true-28     1.000Ki ± 0%     1.000Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=last/ascii=false-28    4.975Ki ± 0%     4.975Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=all/ascii=true-28      1.000Ki ± 0%     1.000Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=all/ascii=false-28     5.712Ki ± 0%     5.712Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=none/ascii=true-28       0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=none/ascii=false-28    17.41Ki ± 0%     17.41Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=first/ascii=true-28    4.000Ki ± 0%     4.000Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=first/ascii=false-28   19.49Ki ± 0%     19.49Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=last/ascii=true-28     4.000Ki ± 0%     4.000Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=last/ascii=false-28    17.89Ki ± 0%     17.89Ki ± 0%         ~ (p=1.000 n=6)
ToNormalizedLower/length=4000/uppercase=all/ascii=true-28      4.000Ki ± 0%     4.000Ki ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=all/ascii=false-28     20.91Ki ± 0%     20.91Ki ± 0%         ~ (p=1.000 n=6) ¹
ZeroOrOneCharacterStringMatcher-28                               0.000 ± 0%       0.000 ± 0%         ~ (p=1.000 n=6) ¹
geomean                                                                     ²                 ?                      ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean

                                                             │  before.txt   │               after.txt                │
                                                             │   allocs/op   │ allocs/op   vs base                    │
FastRegexMatcher/#00-28                                         0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/()-28                                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo-28                                         0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo()-28                                       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^foo-28                                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(foo|bar)-28                                   0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.*-28                                       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*foo-28                                       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^.*foo$-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^.+foo$-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.?-28                                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*-28                                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/().*-28                                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*()-28                                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/().*()-28                                      0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+-28                                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+()-28                                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.+-28                                       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+foo-28                                       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo_.+-28                                      0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo_.*-28                                      0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*foo.*-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.+foo.+-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?s:.*)-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?s:.+)-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?s:^.*foo$)-28                                0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:foo)-28                                    0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(foo|bar))-28                              0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(foo1|foo2|bar))-28                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^(?i:foo|oo)|(bar)$-28                         0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(foo1|foo2|aaa|bbb|ccc|ddd|e-28           18.000 ± 0%     3.000 ± 0%   -83.33% (p=0.002 n=6)
FastRegexMatcher/((.*)(bar|b|buzz)(.+)|foo)$-28                 0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/^$-28                                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(prometheus|api_prom)_api_v1_.+-28             0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/10\.0\.(1|2)\.+-28                             0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/10\.0\.(1|2).+-28                              0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/((fo(bar))|.+foo)-28                           0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/zQPbMkNO|NNSPdvMi|iWuuSoAl|qbvKM-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/jyyfj00j0061|jyyfj00j0062|jyyfj9-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(zQPbMkNO|NNSPdvMi|iWuuSoAl|-28           18.000 ± 0%     3.000 ± 0%   -83.33% (p=0.002 n=6)
FastRegexMatcher/(?i:(AAAAAAAAAAAAAAAAAAAAAAAA|BB-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuS-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(?i:(zQPbMkNO.*|NNSPdvMi.*|iWuuS#01-28         18.00 ± 0%      0.00 ± 0%  -100.00% (p=0.002 n=6)
FastRegexMatcher/(?i:(.*zQPbMkNO|.*NNSPdvMi|.*iWu-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/fo.?-28                                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.?-28                                       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/f.?o-28                                        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*foo.?-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.?foo.+-28                                     0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/foo.?|bar-28                                   0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/ſſs-28                                         0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/.*-.*-.*-.*-.*-28                              0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/(.+)-(.+)-(.+)-(.+)-(.+)-28                    0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/((.*))(?i:f)((.*))o((.*))o((.*))-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
FastRegexMatcher/((.*))f((.*))(?i:o)((.*))o((.*))-28            0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=none/ascii=true-28        0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=none/ascii=false-28       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=first/ascii=true-28       1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=first/ascii=false-28      1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=last/ascii=true-28        1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=last/ascii=false-28       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=10/uppercase=all/ascii=true-28         1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=10/uppercase=all/ascii=false-28        1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=none/ascii=true-28       0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=none/ascii=false-28      4.000 ± 0%     4.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=first/ascii=true-28      1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=first/ascii=false-28     5.000 ± 0%     4.000 ± 0%   -20.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=last/ascii=true-28       1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=last/ascii=false-28      4.000 ± 0%     4.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=100/uppercase=all/ascii=true-28        1.000 ± 0%     0.000 ± 0%  -100.00% (p=0.002 n=6)
ToNormalizedLower/length=100/uppercase=all/ascii=false-28       5.000 ± 0%     5.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=none/ascii=true-28      0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=none/ascii=false-28     4.000 ± 0%     4.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=first/ascii=true-28     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=first/ascii=false-28    5.000 ± 0%     5.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=last/ascii=true-28      1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=last/ascii=false-28     4.000 ± 0%     4.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=all/ascii=true-28       1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=1000/uppercase=all/ascii=false-28      5.000 ± 0%     5.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=none/ascii=true-28      0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=none/ascii=false-28     4.000 ± 0%     4.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=first/ascii=true-28     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=first/ascii=false-28    5.000 ± 0%     5.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=last/ascii=true-28      1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=last/ascii=false-28     4.000 ± 0%     4.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=all/ascii=true-28       1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=6) ¹
ToNormalizedLower/length=4000/uppercase=all/ascii=false-28      5.000 ± 0%     5.000 ± 0%         ~ (p=1.000 n=6) ¹
ZeroOrOneCharacterStringMatcher-28                              0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=6) ¹
geomean                                                                    ²               ?                      ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean

@bboreham bboreham force-pushed the faster-regexp-prefixes branch 2 times, most recently from f9ee6fd to e4f85f7 Compare October 31, 2024 09:19
@bboreham bboreham marked this pull request as ready for review October 31, 2024 09:28
@bboreham bboreham force-pushed the faster-regexp-prefixes branch from 30ca2ce to 5e6f0bd Compare November 4, 2024 12:23
Given a regex like `a(b|c).*`, find ab and ac as prefixes.
Even though the original may have looked like `ab.*|ac.*`, Go extracts
the sub-prefixes, but we want the longer strings when the optimisation
to put them into a map applies.

Signed-off-by: Bryan Boreham <[email protected]>
Up to 32-byte values this saves garbage, runs faster.
For prefixes, only `toLower` the part we need for the map lookup.

Split toNormalisedLower into fast and slow paths, to avoid a penalty
for the `copy` call in the case where no allocations are done.

Signed-off-by: Bryan Boreham <[email protected]>
The case-sensitive path goes ~10% faster if we don't stop to ask whether
it is insensitive.

Signed-off-by: Bryan Boreham <[email protected]>
@bboreham bboreham force-pushed the faster-regexp-prefixes branch from 5e6f0bd to 0f12cf6 Compare November 8, 2024 11:50
Copy link
Collaborator

@pracucci pracucci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting PR. I think the overall idea is nice, but I think I've found a potential issue. Generally speaking, I think we're lacking some tests around edge cases that could be introduced by these changes. Also the fact that I've run fuzzy tests for 5m and they passed, but my simple test has failed is a bit concerning about the tests coverage we have here :|

m.stringMatcher = newMatcherFromPrefixMatchers(caseSensitive, prefixes)
case len(matches) > 0 && len(prefixes) == 0 && caseSensitive:
m.setMatches = matches
m.stringMatcher = stringMatcherFromRegexp(parsed)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] I think this is superfluous and can be removed to keep code easier to follow. If there are setMatches we only use them in matcher (see compileMatchStringFunction()) so stringMatcher is never used.

return len(m.setMatches) > 0 || m.stringMatcher != nil || m.prefix != "" || m.suffix != "" || len(m.contains) > 0
}

// findSetMatches extract equality matches from a regexp.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] This comment needs to be updated. It returns prefixes too now.


func findSetMatchesFromAlternate(re *syntax.Regexp, base string) (matches []string, matchesCaseSensitive bool) {
func findSetMatchesFromAlternate(re *syntax.Regexp, base string) (matches []string, prefixes []prefixMatcher, matchesCaseSensitive bool) {
matches = []string{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? 🤔

if i > 0 && re.Sub[i].Op == syntax.OpStar && len(matches) > 0 {
prefixes = make([]prefixMatcher, 0, len(matches))
for _, prefix := range matches {
right := stringMatcherFromRegexpInternal(re.Sub[i])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we're matching re.Sub[i] as the right side, but what if i is not the last sub of regexp and there are others?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example this test fails:

	const pattern = "a.*a|b.*b|c.*c|d.*d|e.*e|f.*f|g.*g|h.*h|i.*i|l.*l|m.*m|n.*n|o.*o|p.*p|q.*q|r.*r|s.*s|t.*t|u.*u|v.*v|z.*z"

	standard := regexp.MustCompile(pattern)
	require.True(t, standard.MatchString("aa"))
	require.True(t, standard.MatchString("aXa"))
	require.False(t, standard.MatchString("aXb"))

	matcher, err := newFastRegexMatcherWithoutCache(pattern)
	require.NoError(t, err)
	require.True(t, matcher.MatchString("aa"))
	require.True(t, matcher.MatchString("aXa"))
	require.False(t, matcher.MatchString("aXb"))

Comment on lines +271 to +273
if i == len(re.Sub)-1 && len(p) > 0 {
prefixes = append(prefixes, p...)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got lost here. What is this used for? Can we have a code commend to explain it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants