Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance and cleanup code #29

Merged
merged 5 commits into from
Sep 24, 2024
Merged

Conversation

psadac
Copy link
Contributor

@psadac psadac commented Sep 22, 2024

  • remove leading and trailing identical runes before comparing
  • use builtin min() function
  • use supported Go versions, update actions to latest version in CI

Long strings with differences at the beginning (long_lead), in the middle (long_middle) or at the end (long_trail) show significant improvements in processing time and memory allocations. When the optimization is ineffective due to different leading and trailing characters (long_diff) there is no change in processing time or memory allocation.

goos: linux
goarch: amd64
pkg: github.com/agnivade/levenshtein
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
                      │  before.txt  │              after.txt              │
                      │    sec/op    │   sec/op     vs base                │
Simple/ASCII-16         134.20n ± 0%   79.03n ± 0%  -41.11% (p=0.000 n=20)
Simple/French-16         254.8n ± 0%   129.7n ± 0%  -49.09% (p=0.000 n=20)
Simple/Nordic-16         500.6n ± 1%   208.0n ± 0%  -58.45% (p=0.000 n=20)
Simple/Long_lead-16     1862.0n ± 0%   209.6n ± 1%  -88.75% (p=0.000 n=20)
Simple/Long_middle-16   3613.0n ± 0%   325.0n ± 0%  -91.00% (p=0.000 n=20)
Simple/Long_trail-16    3911.0n ± 0%   399.0n ± 1%  -89.80% (p=0.000 n=20)
Simple/Long_diff-16      4.030µ ± 0%   4.029µ ± 1%        ~ (p=0.899 n=20)
Simple/Tibetan-16        413.0n ± 0%   277.3n ± 0%  -32.86% (p=0.000 n=20)
geomean                  964.6n        299.5n       -68.95%

                      │  before.txt  │              after.txt               │
                      │     B/op     │    B/op     vs base                  │
Simple/ASCII-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/French-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Nordic-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Long_lead-16     464.0 ± 0%     368.0 ± 0%  -20.69% (p=0.000 n=20)
Simple/Long_middle-16   672.0 ± 0%     544.0 ± 0%  -19.05% (p=0.000 n=20)
Simple/Long_trail-16    720.0 ± 0%     576.0 ± 0%  -20.00% (p=0.000 n=20)
Simple/Long_diff-16     720.0 ± 0%     720.0 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Tibetan-16       0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
geomean                            ²                -7.99%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                      │  before.txt  │              after.txt               │
                      │  allocs/op   │ allocs/op   vs base                  │
Simple/ASCII-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/French-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Nordic-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Long_lead-16     3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Long_middle-16   3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Long_trail-16    3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Long_diff-16     3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Tibetan-16       0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
geomean                            ²               -14.11%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

benchstat does not indicate any performance regression.

goos: linux
goarch: amd64
pkg: github.com/agnivade/levenshtein
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
                       │ before.txt  │             after.txt              │
                       │   sec/op    │   sec/op     vs base               │
Simple/ASCII-16          134.3n ± 0%   134.5n ± 0%       ~ (p=0.135 n=20)
Simple/French-16         253.8n ± 0%   253.5n ± 0%       ~ (p=0.155 n=20)
Simple/Nordic-16         499.4n ± 0%   498.1n ± 0%  -0.26% (p=0.005 n=20)
Simple/long_string-16    1.853µ ± 0%   1.852µ ± 0%       ~ (p=0.878 n=20)
Simple/Tibetan-16        414.3n ± 0%   414.4n ± 0%       ~ (p=0.952 n=20)
All/ASCII/agniva-16      136.4n ± 0%   136.4n ± 0%       ~ (p=0.941 n=20)
All/ASCII/arbovm-16      193.0n ± 0%   192.6n ± 0%       ~ (p=0.151 n=20)
All/ASCII/dgryski-16     198.6n ± 0%   198.8n ± 1%       ~ (p=0.292 n=20)
All/French/agniva-16     255.9n ± 0%   255.9n ± 0%       ~ (p=0.899 n=20)
All/French/arbovm-16     332.1n ± 0%   331.6n ± 0%       ~ (p=0.857 n=20)
All/French/dgryski-16    333.2n ± 0%   333.1n ± 0%       ~ (p=0.732 n=20)
All/Nordic/agniva-16     500.1n ± 0%   499.9n ± 0%       ~ (p=0.673 n=20)
All/Nordic/arbovm-16     604.1n ± 0%   606.6n ± 0%  +0.41% (p=0.041 n=20)
All/Nordic/dgryski-16    612.4n ± 0%   612.4n ± 0%       ~ (p=0.984 n=20)
All/Tibetan/agniva-16    414.4n ± 0%   414.5n ± 0%       ~ (p=0.753 n=20)
All/Tibetan/arbovm-16    501.4n ± 1%   499.1n ± 1%       ~ (p=0.262 n=20)
All/Tibetan/dgryski-16   504.9n ± 0%   504.8n ± 0%       ~ (p=0.888 n=20)
geomean                  365.7n        365.6n       -0.03%
Benchmarks show approximately a 30% improvement. It's probably overestimated due to the lack of randomness of test inputs.

goos: linux
goarch: amd64
pkg: github.com/agnivade/levenshtein
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
                       │  before.txt  │             after.txt               │
                       │    sec/op    │   sec/op     vs base                │
Simple/ASCII-16          133.80n ± 1%   78.92n ± 0%  -41.02% (p=0.000 n=20)
Simple/French-16          253.8n ± 0%   128.1n ± 0%  -49.50% (p=0.000 n=20)
Simple/Nordic-16          494.8n ± 0%   205.8n ± 0%  -58.41% (p=0.000 n=20)
Simple/long_string-16    1847.5n ± 0%   208.2n ± 0%  -88.73% (p=0.000 n=20)
Simple/Tibetan-16         410.5n ± 0%   277.8n ± 1%  -32.34% (p=0.000 n=20)
All/ASCII/agniva-16      135.30n ± 0%   79.38n ± 0%  -41.33% (p=0.000 n=20)
All/ASCII/arbovm-16       192.0n ± 0%   191.0n ± 1%   -0.52% (p=0.015 n=20)
All/ASCII/dgryski-16      198.4n ± 0%   196.0n ± 0%   -1.21% (p=0.000 n=20)
All/French/agniva-16      253.4n ± 0%   128.8n ± 0%  -49.16% (p=0.000 n=20)
All/French/arbovm-16      330.5n ± 0%   319.7n ± 0%   -3.25% (p=0.000 n=20)
All/French/dgryski-16     331.2n ± 0%   332.2n ± 0%        ~ (p=0.092 n=20)
All/Nordic/agniva-16      495.2n ± 0%   206.9n ± 0%  -58.21% (p=0.000 n=20)
All/Nordic/arbovm-16      600.2n ± 0%   588.1n ± 0%   -2.01% (p=0.000 n=20)
All/Nordic/dgryski-16     609.2n ± 0%   607.8n ± 0%   -0.24% (p=0.020 n=20)
All/Tibetan/agniva-16     409.2n ± 0%   275.8n ± 0%  -32.60% (p=0.000 n=20)
All/Tibetan/arbovm-16     497.5n ± 1%   483.9n ± 0%   -2.74% (p=0.000 n=20)
All/Tibetan/dgryski-16    503.9n ± 1%   498.1n ± 0%   -1.15% (p=0.000 n=20)
geomean                   363.5n        237.3n       -34.71%

                       │  before.txt  │              after.txt               │
                       │     B/op     │    B/op     vs base                  │
Simple/ASCII-16          0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/French-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Nordic-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/long_string-16    464.0 ± 0%     368.0 ± 0%  -20.69% (p=0.000 n=20)
Simple/Tibetan-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/ASCII/agniva-16      0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/ASCII/arbovm-16      96.00 ± 0%     96.00 ± 0%        ~ (p=1.000 n=20) ¹
All/ASCII/dgryski-16     96.00 ± 0%     96.00 ± 0%        ~ (p=1.000 n=20) ¹
All/French/agniva-16     0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/French/arbovm-16     128.0 ± 0%     128.0 ± 0%        ~ (p=1.000 n=20) ¹
All/French/dgryski-16    128.0 ± 0%     128.0 ± 0%        ~ (p=1.000 n=20) ¹
All/Nordic/agniva-16     0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Nordic/arbovm-16     192.0 ± 0%     192.0 ± 0%        ~ (p=1.000 n=20) ¹
All/Nordic/dgryski-16    192.0 ± 0%     192.0 ± 0%        ~ (p=1.000 n=20) ¹
All/Tibetan/agniva-16    0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Tibetan/arbovm-16    160.0 ± 0%     160.0 ± 0%        ~ (p=1.000 n=20) ¹
All/Tibetan/dgryski-16   160.0 ± 0%     160.0 ± 0%        ~ (p=1.000 n=20) ¹
geomean                             ²                -1.35%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                       │  before.txt  │              after.txt               │
                       │  allocs/op   │ allocs/op   vs base                  │
Simple/ASCII-16          0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/French-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Nordic-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/long_string-16    3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Tibetan-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/ASCII/agniva-16      0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/ASCII/arbovm-16      1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/ASCII/dgryski-16     1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/French/agniva-16     0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/French/arbovm-16     1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/French/dgryski-16    1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Nordic/agniva-16     0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Nordic/arbovm-16     1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Nordic/dgryski-16    1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Tibetan/agniva-16    0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Tibetan/arbovm-16    1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
All/Tibetan/dgryski-16   1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=20) ¹
geomean                             ²                -2.36%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
Copy link
Owner

@agnivade agnivade left a comment

Choose a reason for hiding this comment

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

Thanks for this. Appreciate the care!

levenshtein.go Show resolved Hide resolved
Benchmarks are run before and after optimization "Remove leading and trailing identical runes".
Long strings with differences at the beginning (long_lead), in the middle (long_middle) or at the end (long_trail) show significant improvements in processing time and memory allocations. When the optimization is ineffective due to different leading and trailing characters (long_diff) there is no change in processing time or memory allocation.

goos: linux
goarch: amd64
pkg: github.com/agnivade/levenshtein
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
                      │  before.txt  │              after.txt              │
                      │    sec/op    │   sec/op     vs base                │
Simple/ASCII-16         134.20n ± 0%   79.03n ± 0%  -41.11% (p=0.000 n=20)
Simple/French-16         254.8n ± 0%   129.7n ± 0%  -49.09% (p=0.000 n=20)
Simple/Nordic-16         500.6n ± 1%   208.0n ± 0%  -58.45% (p=0.000 n=20)
Simple/Long_lead-16     1862.0n ± 0%   209.6n ± 1%  -88.75% (p=0.000 n=20)
Simple/Long_middle-16   3613.0n ± 0%   325.0n ± 0%  -91.00% (p=0.000 n=20)
Simple/Long_trail-16    3911.0n ± 0%   399.0n ± 1%  -89.80% (p=0.000 n=20)
Simple/Long_diff-16      4.030µ ± 0%   4.029µ ± 1%        ~ (p=0.899 n=20)
Simple/Tibetan-16        413.0n ± 0%   277.3n ± 0%  -32.86% (p=0.000 n=20)
geomean                  964.6n        299.5n       -68.95%

                      │  before.txt  │              after.txt               │
                      │     B/op     │    B/op     vs base                  │
Simple/ASCII-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/French-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Nordic-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Long_lead-16     464.0 ± 0%     368.0 ± 0%  -20.69% (p=0.000 n=20)
Simple/Long_middle-16   672.0 ± 0%     544.0 ± 0%  -19.05% (p=0.000 n=20)
Simple/Long_trail-16    720.0 ± 0%     576.0 ± 0%  -20.00% (p=0.000 n=20)
Simple/Long_diff-16     720.0 ± 0%     720.0 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Tibetan-16       0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
geomean                            ²                -7.99%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                      │  before.txt  │              after.txt               │
                      │  allocs/op   │ allocs/op   vs base                  │
Simple/ASCII-16         0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/French-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Nordic-16        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Long_lead-16     3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Long_middle-16   3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Long_trail-16    3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.000 n=20)
Simple/Long_diff-16     3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=20) ¹
Simple/Tibetan-16       0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=20) ¹
geomean                            ²               -14.11%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
@agnivade
Copy link
Owner

Sorry, I had removed Travis, but somehow it still seems to linger.

Anyways, just one little thing: Could you please update the PR message with the full benchmark results from all the tests? Just for posterity. Thank you.

@psadac
Copy link
Contributor Author

psadac commented Sep 24, 2024

I updated the message for "posterity". For your Travis problem it may be in the webhooks.

@agnivade
Copy link
Owner

There are no webhooks anymore. I deleted everything. Weird. Anyways, thanks for the PR!

@agnivade agnivade merged commit fac16fe into agnivade:master Sep 24, 2024
6 checks passed
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