Skip to content

Comments

Improve IPv4 lookups with Eytzinger index#202

Open
marselester wants to merge 1 commit intooschwald:mainfrom
marselester:ipv4index
Open

Improve IPv4 lookups with Eytzinger index#202
marselester wants to merge 1 commit intooschwald:mainfrom
marselester:ipv4index

Conversation

@marselester
Copy link

@marselester marselester commented Jan 30, 2026

Just an idea to use IPv4 index (1 MB) to speed up lookups by almost 30%.

benchstat
$ git checkout main
$ go test -count=1 -run=_ -bench=^ -benchmem
$ go test -count=10 -run=_ -bench=^ -benchmem ./... > old.txt

$ git checkout ipv4index
$ go test -count=1 -run=_ -bench=^ -benchmem
$ go test -count=10 -run=_ -bench=^ -benchmem ./... > new.txt

$ benchstat old.txt new.txt 
goos: darwin
goarch: arm64
pkg: github.com/oschwald/maxminddb-golang/v2
cpu: Apple M2 Pro
                                           │   old.txt    │               new.txt               │
                                           │    sec/op    │   sec/op     vs base                │
Open-10                                       50.69µ ± 0%   51.52µ ± 0%   +1.64% (p=0.000 n=10)
InterfaceLookup-10                            6.724µ ± 1%   6.895µ ± 1%   +2.54% (p=0.000 n=10)
LookupNetwork-10                             114.05n ± 1%   81.42n ± 1%  -28.61% (p=0.000 n=10)
CityLookup-10                                 4.013µ ± 0%   4.026µ ± 2%   +0.31% (p=0.050 n=10)
CityLookupOnly-10                            110.40n ± 5%   77.82n ± 0%  -29.52% (p=0.000 n=10)
DecodeCountryCodeWithStruct-10                515.3n ± 1%   526.0n ± 1%   +2.08% (p=0.005 n=10)
DecodePathCountryCode-10                      256.8n ± 5%   266.2n ± 1%   +3.64% (p=0.017 n=10)
CityLookupConcurrent/goroutines_1-10          429.0µ ± 1%   429.5µ ± 1%        ~ (p=0.190 n=10)
CityLookupConcurrent/goroutines_4-10          1.071m ± 0%   1.083m ± 1%   +1.15% (p=0.000 n=10)
CityLookupConcurrent/goroutines_16-10         4.065m ± 0%   4.023m ± 1%   -1.04% (p=0.002 n=10)
CityLookupConcurrent/goroutines_64-10         17.82m ± 0%   17.73m ± 1%        ~ (p=0.218 n=10)
Networks-10                                   80.51µ ± 0%   80.44µ ± 0%        ~ (p=0.436 n=10)
SkipEmptyValues/without_SkipEmptyValues-10    11.10µ ± 1%   10.93µ ± 0%   -1.51% (p=0.000 n=10)
SkipEmptyValues/with_SkipEmptyValues-10       9.235µ ± 0%   9.172µ ± 0%   -0.68% (p=0.002 n=10)
geomean                                       18.67µ        17.87µ        -4.28%

                                           │    old.txt     │                new.txt                │
                                           │      B/op      │     B/op      vs base                 │
Open-10                                      54.60Ki ± 0%     54.62Ki ± 0%  +0.03% (p=0.000 n=10)
InterfaceLookup-10                           3.852Ki ± 0%     3.854Ki ± 0%       ~ (p=0.382 n=10)
LookupNetwork-10                               0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=10) ¹
CityLookup-10                                  384.0 ± 0%       385.0 ± 0%       ~ (p=0.064 n=10)
CityLookupOnly-10                              0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=10) ¹
DecodeCountryCodeWithStruct-10                 6.000 ± 0%       6.000 ± 0%       ~ (p=1.000 n=10) ¹
DecodePathCountryCode-10                       6.000 ± 0%       6.000 ± 0%       ~ (p=1.000 n=10) ¹
CityLookupConcurrent/goroutines_1-10         46.92Ki ± 0%     46.90Ki ± 0%       ~ (p=0.566 n=10)
CityLookupConcurrent/goroutines_4-10         188.6Ki ± 0%     188.7Ki ± 0%       ~ (p=0.247 n=10)
CityLookupConcurrent/goroutines_16-10        755.9Ki ± 0%     756.1Ki ± 0%       ~ (p=0.436 n=10)
CityLookupConcurrent/goroutines_64-10        2.967Mi ± 0%     2.965Mi ± 0%       ~ (p=0.631 n=10)
Networks-10                                  8.062Ki ± 0%     8.062Ki ± 0%       ~ (p=1.000 n=10) ¹
SkipEmptyValues/without_SkipEmptyValues-10   5.289Ki ± 0%     5.289Ki ± 0%       ~ (p=1.000 n=10) ¹
SkipEmptyValues/with_SkipEmptyValues-10      5.289Ki ± 0%     5.289Ki ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                   ²                 +0.02%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                           │    old.txt    │               new.txt                │
                                           │   allocs/op   │  allocs/op   vs base                 │
Open-10                                       35.00 ± 0%      36.00 ± 0%  +2.86% (p=0.000 n=10)
InterfaceLookup-10                            81.00 ± 0%      81.00 ± 0%       ~ (p=1.000 n=10) ¹
LookupNetwork-10                              0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=10) ¹
CityLookup-10                                 16.00 ± 0%      16.00 ± 0%       ~ (p=1.000 n=10) ¹
CityLookupOnly-10                             0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=10) ¹
DecodeCountryCodeWithStruct-10                0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=10) ¹
DecodePathCountryCode-10                      0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=10) ¹
CityLookupConcurrent/goroutines_1-10         1.679k ± 0%     1.679k ± 0%       ~ (p=0.675 n=10)
CityLookupConcurrent/goroutines_4-10         6.820k ± 0%     6.824k ± 0%       ~ (p=0.088 n=10)
CityLookupConcurrent/goroutines_16-10        27.58k ± 0%     27.59k ± 0%       ~ (p=0.492 n=10)
CityLookupConcurrent/goroutines_64-10        112.3k ± 0%     112.2k ± 0%       ~ (p=0.579 n=10)
Networks-10                                   349.0 ± 0%      349.0 ± 0%       ~ (p=1.000 n=10) ¹
SkipEmptyValues/without_SkipEmptyValues-10    4.000 ± 0%      4.000 ± 0%       ~ (p=1.000 n=10) ¹
SkipEmptyValues/with_SkipEmptyValues-10       4.000 ± 0%      4.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                  ²                +0.20%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                      │   old.txt   │              new.txt               │
                                      │ lookups/sec │ lookups/sec  vs base               │
CityLookupConcurrent/goroutines_1-10    233.1k ± 1%   232.8k ± 1%       ~ (p=0.190 n=10)
CityLookupConcurrent/goroutines_4-10    373.5k ± 0%   369.3k ± 1%  -1.14% (p=0.000 n=10)
CityLookupConcurrent/goroutines_16-10   393.6k ± 0%   397.7k ± 1%  +1.05% (p=0.002 n=10)
CityLookupConcurrent/goroutines_64-10   359.1k ± 0%   360.9k ± 1%       ~ (p=0.218 n=10)
geomean                                 333.1k        333.3k       +0.07%

pkg: github.com/oschwald/maxminddb-golang/v2/internal/decoder
                       │   old.txt   │              new.txt               │
                       │   sec/op    │   sec/op     vs base               │
WrapError_HappyPath-10   1.956n ± 1%   1.918n ± 1%  -1.89% (p=0.001 n=10)
WrapError_ErrorPath-10   32.38n ± 0%   32.33n ± 0%       ~ (p=0.617 n=10)
StructDecoding-10        91.53n ± 0%   92.88n ± 0%  +1.46% (p=0.000 n=10)
SimpleDecoding-10        92.82n ± 1%   92.53n ± 1%  -0.32% (p=0.012 n=10)
FieldLookup-10           73.11n ± 0%   73.02n ± 0%       ~ (p=0.059 n=10)
geomean                  33.03n        32.96n       -0.21%

                       │   old.txt    │               new.txt               │
                       │     B/op     │    B/op     vs base                 │
WrapError_HappyPath-10   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
WrapError_ErrorPath-10   64.00 ± 0%     64.00 ± 0%       ~ (p=1.000 n=10) ¹
StructDecoding-10        24.00 ± 0%     24.00 ± 0%       ~ (p=1.000 n=10) ¹
SimpleDecoding-10        24.00 ± 0%     24.00 ± 0%       ~ (p=1.000 n=10) ¹
FieldLookup-10           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean                             ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                       │   old.txt    │               new.txt               │
                       │  allocs/op   │ allocs/op   vs base                 │
WrapError_HappyPath-10   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
WrapError_ErrorPath-10   2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=10) ¹
StructDecoding-10        2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=10) ¹
SimpleDecoding-10        2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=10) ¹
FieldLookup-10           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean                             ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
old.txt
goos: darwin
goarch: arm64
pkg: github.com/oschwald/maxminddb-golang/v2
cpu: Apple M2 Pro
BenchmarkOpen-10                           	   23210	     50662 ns/op	   55913 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23799	     50550 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23458	     51000 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23467	     50910 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23562	     50767 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23608	     50717 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23700	     50669 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23823	     50467 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23715	     50766 ns/op	   55912 B/op	      35 allocs/op
BenchmarkOpen-10                           	   23724	     50657 ns/op	   55912 B/op	      35 allocs/op
BenchmarkInterfaceLookup-10                	  168498	      6724 ns/op	    3947 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  176178	      6691 ns/op	    3951 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  172395	      6708 ns/op	    3942 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  174866	      6724 ns/op	    3942 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  177483	      6698 ns/op	    3936 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  174525	      6800 ns/op	    3948 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  173733	      6758 ns/op	    3952 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  175371	      6730 ns/op	    3943 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  172083	      6743 ns/op	    3945 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  176066	      6716 ns/op	    3940 B/op	      81 allocs/op
BenchmarkLookupNetwork-10                  	10090386	       114.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10481210	       113.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10490338	       115.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10326555	       114.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10420838	       122.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10443420	       114.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10484583	       113.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10457671	       113.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10392474	       114.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	10439971	       113.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookup-10                     	  285435	      3995 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  288700	      4039 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  287544	      4026 ns/op	     386 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  289695	      3997 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  288228	      4013 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  291012	      4006 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  288615	      4018 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  284725	      4031 ns/op	     385 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  290787	      4014 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  286382	      4006 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookupOnly-10                 	10502806	       115.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10744716	       110.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10732227	       110.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10803717	       110.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10806124	       110.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10575804	       110.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10730908	       110.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10780588	       110.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10732011	       116.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	10819155	       113.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2317465	       513.6 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2314998	       513.2 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2343344	       510.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2347916	       510.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2346416	       518.7 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2270030	       517.2 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2341980	       511.0 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2344264	       517.0 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2344375	       519.7 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2080105	       575.2 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4299894	       274.7 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4624309	       256.2 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4647848	       257.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4636840	       256.8 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4656048	       257.2 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4648267	       256.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4588400	       269.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4643652	       256.8 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4648645	       256.7 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4659080	       256.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2659	    427554 ns/op	    233889 lookups/sec	   48021 B/op	    1679 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2802	    429233 ns/op	    232974 lookups/sec	   48089 B/op	    1681 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2822	    428197 ns/op	    233537 lookups/sec	   48075 B/op	    1680 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2792	    428454 ns/op	    233397 lookups/sec	   47978 B/op	    1677 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2796	    432989 ns/op	    230953 lookups/sec	   48144 B/op	    1682 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2632	    434367 ns/op	    230220 lookups/sec	   47960 B/op	    1677 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2768	    434012 ns/op	    230409 lookups/sec	   47978 B/op	    1677 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2782	    429228 ns/op	    232976 lookups/sec	   48211 B/op	    1685 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2785	    426295 ns/op	    234579 lookups/sec	   47926 B/op	    1675 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2767	    428786 ns/op	    233217 lookups/sec	   48089 B/op	    1681 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1117	   1066069 ns/op	    375210 lookups/sec	  193277 B/op	    6821 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1117	   1066174 ns/op	    375173 lookups/sec	  193156 B/op	    6819 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1126	   1064235 ns/op	    375857 lookups/sec	  192724 B/op	    6808 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1135	   1071630 ns/op	    373263 lookups/sec	  193178 B/op	    6823 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1116	   1070310 ns/op	    373724 lookups/sec	  193026 B/op	    6819 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1131	   1070699 ns/op	    373588 lookups/sec	  192746 B/op	    6809 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1129	   1071642 ns/op	    373259 lookups/sec	  192938 B/op	    6813 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1111	   1071062 ns/op	    373461 lookups/sec	  193310 B/op	    6826 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1123	   1071388 ns/op	    373348 lookups/sec	  193165 B/op	    6822 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1119	   1077429 ns/op	    371254 lookups/sec	  193209 B/op	    6827 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     296	   4067405 ns/op	    393371 lookups/sec	  773907 B/op	   27587 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     295	   4049980 ns/op	    395064 lookups/sec	  774344 B/op	   27594 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     295	   4071672 ns/op	    392960 lookups/sec	  775199 B/op	   27620 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     292	   4054794 ns/op	    394595 lookups/sec	  774574 B/op	   27580 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     294	   4046675 ns/op	    395386 lookups/sec	  772588 B/op	   27531 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     286	   4085727 ns/op	    391607 lookups/sec	  772698 B/op	   27533 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     294	   4125359 ns/op	    387845 lookups/sec	  774274 B/op	   27599 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     294	   4074792 ns/op	    392660 lookups/sec	  773520 B/op	   27570 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     294	   4041982 ns/op	    395846 lookups/sec	  774517 B/op	   27601 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     294	   4063486 ns/op	    393751 lookups/sec	  771923 B/op	   27497 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17756997 ns/op	    360421 lookups/sec	 3108549 B/op	  112076 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17722113 ns/op	    361131 lookups/sec	 3113148 B/op	  112348 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      69	  17867190 ns/op	    358199 lookups/sec	 3104997 B/op	  112072 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17904180 ns/op	    357459 lookups/sec	 3103930 B/op	  111872 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17856052 ns/op	    358422 lookups/sec	 3109055 B/op	  112218 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      69	  17801790 ns/op	    359515 lookups/sec	 3104557 B/op	  111945 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17819586 ns/op	    359156 lookups/sec	 3116409 B/op	  112447 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17780879 ns/op	    359937 lookups/sec	 3115576 B/op	  112344 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17827780 ns/op	    358992 lookups/sec	 3113184 B/op	  112338 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17842754 ns/op	    358689 lookups/sec	 3113042 B/op	  112354 allocs/op
BenchmarkNetworks-10                                  	   14888	     80497 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14906	     80492 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14913	     80443 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14871	     80613 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14762	     81076 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14914	     80452 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14922	     80522 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14863	     80638 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14914	     80435 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14887	     80553 ns/op	    8256 B/op	     349 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  107853	     11203 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  107504	     11094 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  108355	     11086 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  108385	     11075 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  107643	     11101 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  108012	     11080 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  108166	     11157 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  108187	     11103 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  108333	     11202 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  107646	     11080 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  129754	      9238 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130689	      9209 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130986	      9250 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130515	      9226 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  129415	      9236 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130741	      9234 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130629	      9235 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  129621	      9252 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  129513	      9222 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  129716	      9220 ns/op	    5416 B/op	       4 allocs/op
PASS
ok  	github.com/oschwald/maxminddb-golang/v2	175.229s
goos: darwin
goarch: arm64
pkg: github.com/oschwald/maxminddb-golang/v2/internal/decoder
cpu: Apple M2 Pro
BenchmarkWrapError_HappyPath-10    	571360326	         1.982 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	613951274	         1.956 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	612433939	         1.956 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	614385843	         1.958 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	612509613	         1.959 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	613861240	         1.954 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	614592603	         1.953 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	615431167	         1.955 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	614554440	         1.940 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	624407300	         1.923 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_ErrorPath-10    	35721195	        32.42 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	36878869	        32.26 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37606354	        32.40 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37087116	        32.27 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37675573	        32.21 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37417222	        32.27 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37205119	        32.86 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	36509304	        32.43 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37194548	        32.39 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37223343	        32.37 ns/op	      64 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13153891	        91.75 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13067251	        91.64 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13088974	        91.99 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13152624	        91.52 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13131376	        91.37 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13100263	        91.38 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13179100	        91.29 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13180596	        91.36 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13176453	        93.11 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13040258	        91.55 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12986503	        92.66 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12965998	        92.82 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12979111	        92.75 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12908118	        92.99 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12893047	        92.79 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12978152	        92.78 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12996236	        93.17 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	13002508	        93.71 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12910275	        92.83 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12618799	        95.40 ns/op	      24 B/op	       2 allocs/op
BenchmarkFieldLookup-10            	16434790	        73.07 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16412538	        73.09 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16361469	        73.10 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16405423	        73.23 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16395103	        73.12 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16403451	        73.33 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16348114	        73.14 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16418666	        73.11 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16346388	        73.10 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16366174	        73.12 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/oschwald/maxminddb-golang/v2/internal/decoder	60.451s
?   	github.com/oschwald/maxminddb-golang/v2/internal/mmdberrors	[no test files]
?   	github.com/oschwald/maxminddb-golang/v2/mmdbdata	[no test files]
new.txt
goos: darwin
goarch: arm64
pkg: github.com/oschwald/maxminddb-golang/v2
cpu: Apple M2 Pro
BenchmarkOpen-10                           	   22916	     51526 ns/op	   55929 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23562	     51213 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23148	     51555 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23355	     51460 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23311	     51410 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23204	     51730 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23176	     51711 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23414	     51511 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23305	     51520 ns/op	   55928 B/op	      36 allocs/op
BenchmarkOpen-10                           	   23276	     51702 ns/op	   55928 B/op	      36 allocs/op
BenchmarkInterfaceLookup-10                	  166940	      6940 ns/op	    3947 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  173491	      7026 ns/op	    3949 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  170977	      6967 ns/op	    3938 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  163531	      6941 ns/op	    3948 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  170925	      6896 ns/op	    3944 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  172881	      6883 ns/op	    3950 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  171848	      6892 ns/op	    3946 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  173041	      6890 ns/op	    3951 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  172366	      6893 ns/op	    3946 B/op	      81 allocs/op
BenchmarkInterfaceLookup-10                	  171249	      6890 ns/op	    3945 B/op	      81 allocs/op
BenchmarkLookupNetwork-10                  	14187876	        81.73 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14709968	        81.32 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14671562	        81.41 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14539718	        81.96 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	13964448	        96.95 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14642546	        81.10 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14470942	        81.22 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14669664	        81.86 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14634465	        81.14 ns/op	       0 B/op	       0 allocs/op
BenchmarkLookupNetwork-10                  	14705671	        81.42 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookup-10                     	  287986	      4044 ns/op	     386 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  292345	      4020 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  294315	      4022 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  294824	      4006 ns/op	     385 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  293499	      4030 ns/op	     386 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  295082	      4126 ns/op	     384 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  288753	      4038 ns/op	     385 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  293091	      4018 ns/op	     385 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  295656	      4115 ns/op	     385 B/op	      16 allocs/op
BenchmarkCityLookup-10                     	  291489	      4014 ns/op	     385 B/op	      16 allocs/op
BenchmarkCityLookupOnly-10                 	14733439	        77.99 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15404809	        77.74 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15350527	        77.70 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15352024	        77.75 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15335642	        78.52 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15199484	        77.88 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15325873	        77.98 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15314049	        77.60 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15343854	        77.74 ns/op	       0 B/op	       0 allocs/op
BenchmarkCityLookupOnly-10                 	15334286	        77.93 ns/op	       0 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2299168	       519.9 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2274604	       526.5 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2302524	       519.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2273664	       525.7 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2304270	       519.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2278878	       527.5 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2309872	       518.6 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2269339	       526.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2305672	       526.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodeCountryCodeWithStruct-10    	 2213518	       532.0 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4504533	       264.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4491945	       266.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4320588	       268.1 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4500500	       273.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4534380	       267.4 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4494543	       266.0 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4449067	       267.2 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4494318	       266.0 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4540120	       263.3 ns/op	       6 B/op	       0 allocs/op
BenchmarkDecodePathCountryCode-10          	 4497115	       265.8 ns/op	       6 B/op	       0 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2727	    437141 ns/op	    228759 lookups/sec	   48079 B/op	    1681 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2785	    429457 ns/op	    232852 lookups/sec	   48035 B/op	    1679 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2756	    433273 ns/op	    230801 lookups/sec	   48097 B/op	    1682 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2785	    429617 ns/op	    232765 lookups/sec	   47959 B/op	    1675 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2839	    429129 ns/op	    233030 lookups/sec	   48019 B/op	    1678 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2760	    430097 ns/op	    232506 lookups/sec	   48054 B/op	    1681 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2802	    429422 ns/op	    232871 lookups/sec	   48009 B/op	    1679 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2773	    429191 ns/op	    232997 lookups/sec	   47873 B/op	    1672 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2792	    428999 ns/op	    233101 lookups/sec	   48044 B/op	    1680 allocs/op
BenchmarkCityLookupConcurrent/goroutines_1-10         	    2778	    432658 ns/op	    231129 lookups/sec	   47979 B/op	    1677 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1113	   1087721 ns/op	    367741 lookups/sec	  193221 B/op	    6825 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1108	   1080225 ns/op	    370293 lookups/sec	  193471 B/op	    6830 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1111	   1082158 ns/op	    369632 lookups/sec	  193447 B/op	    6839 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1114	   1083181 ns/op	    369283 lookups/sec	  193481 B/op	    6836 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1113	   1103303 ns/op	    362548 lookups/sec	  193127 B/op	    6823 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1102	   1082393 ns/op	    369552 lookups/sec	  193075 B/op	    6819 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1126	   1085253 ns/op	    368578 lookups/sec	  193208 B/op	    6829 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1110	   1095235 ns/op	    365218 lookups/sec	  192929 B/op	    6814 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1107	   1083184 ns/op	    369282 lookups/sec	  193193 B/op	    6823 allocs/op
BenchmarkCityLookupConcurrent/goroutines_4-10         	    1107	   1080966 ns/op	    370039 lookups/sec	  193029 B/op	    6816 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     298	   4031846 ns/op	    396841 lookups/sec	  774156 B/op	   27593 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     296	   4021089 ns/op	    397902 lookups/sec	  773736 B/op	   27575 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     296	   4090967 ns/op	    391106 lookups/sec	  774761 B/op	   27599 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     298	   4008309 ns/op	    399171 lookups/sec	  774405 B/op	   27591 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     295	   4044080 ns/op	    395640 lookups/sec	  773771 B/op	   27565 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     298	   4037932 ns/op	    396243 lookups/sec	  776065 B/op	   27665 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     296	   4010718 ns/op	    398931 lookups/sec	  774509 B/op	   27601 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     298	   4021729 ns/op	    397839 lookups/sec	  772300 B/op	   27533 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     300	   4003348 ns/op	    399666 lookups/sec	  774065 B/op	   27568 allocs/op
BenchmarkCityLookupConcurrent/goroutines_16-10        	     297	   4024440 ns/op	    397571 lookups/sec	  774907 B/op	   27618 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17594339 ns/op	    363753 lookups/sec	 3114024 B/op	  112409 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      70	  17703700 ns/op	    361506 lookups/sec	 3110598 B/op	  112149 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      68	  17866270 ns/op	    358217 lookups/sec	 3108936 B/op	  112160 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      70	  17759439 ns/op	    360372 lookups/sec	 3108104 B/op	  112115 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      66	  17658666 ns/op	    362428 lookups/sec	 3098569 B/op	  111743 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      67	  17809084 ns/op	    359367 lookups/sec	 3107358 B/op	  112185 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      64	  17702378 ns/op	    361533 lookups/sec	 3116220 B/op	  112411 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      66	  17890715 ns/op	    357728 lookups/sec	 3104909 B/op	  112045 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      70	  17935577 ns/op	    356833 lookups/sec	 3103803 B/op	  111909 allocs/op
BenchmarkCityLookupConcurrent/goroutines_64-10        	      66	  17684297 ns/op	    361903 lookups/sec	 3115930 B/op	  112326 allocs/op
BenchmarkNetworks-10                                  	   14896	     80481 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14959	     80254 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14859	     80791 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14956	     80250 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14869	     80701 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14918	     80405 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14871	     80723 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14934	     80330 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14856	     80814 ns/op	    8256 B/op	     349 allocs/op
BenchmarkNetworks-10                                  	   14948	     80258 ns/op	    8256 B/op	     349 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109106	     10922 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109563	     10952 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109432	     10942 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  110391	     10919 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109620	     10921 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109707	     10916 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109796	     10932 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109742	     10929 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  109756	     10930 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/without_SkipEmptyValues-10   	  110127	     10961 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  131112	      9164 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130227	      9172 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130442	      9303 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130791	      9174 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  131308	      9170 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  129435	      9163 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130803	      9167 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  131026	      9179 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  130465	      9211 ns/op	    5416 B/op	       4 allocs/op
BenchmarkSkipEmptyValues/with_SkipEmptyValues-10      	  132009	      9171 ns/op	    5416 B/op	       4 allocs/op
PASS
ok  	github.com/oschwald/maxminddb-golang/v2	175.751s
goos: darwin
goarch: arm64
pkg: github.com/oschwald/maxminddb-golang/v2/internal/decoder
cpu: Apple M2 Pro
BenchmarkWrapError_HappyPath-10    	572191948	         1.958 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	607400540	         1.937 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	625760878	         1.926 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	623400775	         1.918 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	626524434	         1.917 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	626322916	         1.916 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	625003118	         1.919 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	626580320	         1.918 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	626573911	         1.918 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_HappyPath-10    	624771404	         1.921 ns/op	       0 B/op	       0 allocs/op
BenchmarkWrapError_ErrorPath-10    	34697498	        32.30 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37272867	        32.22 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37203966	        32.25 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37208965	        32.38 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37210936	        32.40 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	36978686	        32.22 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	36706039	        32.37 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	35930967	        32.93 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	37430889	        32.25 ns/op	      64 B/op	       2 allocs/op
BenchmarkWrapError_ErrorPath-10    	36460125	        32.46 ns/op	      64 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13002837	        93.23 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12966740	        92.49 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12896764	        93.31 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12953460	        92.90 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12991933	        93.52 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12695300	        92.78 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12934094	        92.85 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	12953821	        93.06 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13002291	        92.46 ns/op	      24 B/op	       2 allocs/op
BenchmarkStructDecoding-10         	13026546	        92.61 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12983318	        92.48 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12947182	        92.47 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12931335	        93.31 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12989770	        92.48 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12950984	        92.66 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	13008064	        92.48 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	13044860	        92.44 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12964539	        92.58 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12693592	        94.13 ns/op	      24 B/op	       2 allocs/op
BenchmarkSimpleDecoding-10         	12870172	        92.63 ns/op	      24 B/op	       2 allocs/op
BenchmarkFieldLookup-10            	16440786	        72.96 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16456202	        73.14 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16488423	        73.27 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16404816	        72.98 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16364416	        73.01 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16452366	        72.99 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16456418	        72.98 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16453268	        73.07 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16453597	        73.18 ns/op	       0 B/op	       0 allocs/op
BenchmarkFieldLookup-10            	16420622	        73.03 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/oschwald/maxminddb-golang/v2/internal/decoder	60.324s
?   	github.com/oschwald/maxminddb-golang/v2/internal/mmdberrors	[no test files]
?   	github.com/oschwald/maxminddb-golang/v2/mmdbdata	[no test files]

Summary by CodeRabbit

  • New Features

    • Added optional IPv4 indexing to enhance address lookup performance.
  • Tests

    • Updated performance benchmarks to evaluate the IPv4 index feature.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

Walkthrough

This pull request introduces an optional IPv4 index feature to optimize IPv4 lookups using an Eytzinger-like precomputed path. A new WithIPv4Index option function enables this optimization. The implementation refactors traversal logic with a startBit-based flow, adds indexed traversal helpers, and updates tests to use the new option in benchmarks.

Changes

Cohort / File(s) Summary
IPv4 Index Optimization
reader.go
Introduces IPv4 index feature with readerOptions struct, WithIPv4Index option function, and IPv4 index construction during reader initialization. Refactors traversal logic with startBit-based flow, adds traverseTreeWithIndex and traverseTreeFromBit helpers, and extends existing traversal helpers (traverseTree24/28/32) to support indexed lookups with fallback to regular traversal.
Benchmark Test Updates
reader_test.go
Updates four benchmark tests (BenchmarkInterfaceLookup, BenchmarkLookupNetwork, BenchmarkCityLookup, BenchmarkCityLookupOnly) to pass WithIPv4Index option to Open call, enabling IPv4 index usage in benchmarks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰✨ Beneath the Eytzinger's winding path,
IPv4 lookups now find swift aftermath—
From sixteen hops, the index leaps ahead,
With fallbacks safe, no traversals left unread! 🏃‍♂️💨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve IPv4 lookups with Eytzinger index' directly and accurately summarizes the main change: introducing an Eytzinger-layout IPv4 index to optimize IPv4 lookup performance.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@reader_test.go`:
- Line 866: Add a functional test that opens the MMDB with the WithIPv4Index
option and verifies the indexed lookup path returns identical results to the
non-indexed path: call Open("GeoLite2-City.mmdb", WithIPv4Index) and compare its
lookup outputs against Open("GeoLite2-City.mmdb") for the same test IPs (add
this either in TestLookupNetwork or as a new TestIPv4IndexLookup that iterates
existing test cases), ensuring you exercise the functions used for
lookups/decoding to validate correctness; also add brief comments to
BenchmarkDecodeCountryCodeWithStruct, BenchmarkDecodePathCountryCode, and
BenchmarkCityLookupConcurrent explaining why WithIPv4Index is intentionally
omitted or add variants that include it for comparison.

In `@reader.go`:
- Around line 503-512: Replace the len-based check with a nil check to clarify
intent: change the conditional that decides between r.traverseTreeWithIndex and
r.traverseTree to use "r.ipv4Index != nil" instead of "len(r.ipv4Index) > 0";
keep the rest of the logic (calling traverseTreeWithIndex(ip) when the index is
present and traverseTree(ip, 0, 128) otherwise) intact to preserve behavior.
- Around line 662-670: The node parameter in Reader.traverseTree is
unused/overwritten (always 0 at callers and replaced when ip.Is4()), so remove
the node uint parameter from traverseTree and update its body to call
r.traverseTreeFromBit(ip, startNode, startBit, stopBit) using r.ipv4Start (and
r.ipv4StartBitDepth) for IPv4 or 0 for IPv6; also update all call sites to drop
the node argument and adjust any signatures that referenced traverseTree
accordingly while keeping traverseTreeFromBit untouched.


func BenchmarkInterfaceLookup(b *testing.B) {
db, err := Open("GeoLite2-City.mmdb")
db, err := Open("GeoLite2-City.mmdb", WithIPv4Index)
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for functional test coverage of WithIPv4Index option

echo "=== WithIPv4Index usage in all test files ==="
rg -n 'WithIPv4Index' --glob '*_test.go'

echo -e "\n=== Test functions (non-benchmark) opening databases ==="
rg -n 'func Test[^B]' --glob '*_test.go' -A 10 | grep -E '(func Test|Open\()'

echo -e "\n=== All Open calls with WithIPv4Index context ==="
rg -n 'Open\(' --glob '*_test.go' -B 2 -A 2 | grep -E '(func|Open\(|WithIPv4Index)'

Repository: oschwald/maxminddb-golang

Length of output: 15896


Add functional tests for the IPv4 index path.

The WithIPv4Index option is only used in benchmarks (lines 866, 885, 956, 975). While these benchmarks validate performance with the index enabled, there are no functional unit tests that exercise the indexed lookup path for correctness. Add test cases to TestLookupNetwork or create a dedicated test that opens the database with WithIPv4Index to ensure the index-based traversal returns identical results to the non-indexed path.

Additionally, some IPv4 benchmarks (BenchmarkDecodeCountryCodeWithStruct, BenchmarkDecodePathCountryCode, BenchmarkCityLookupConcurrent) do not use WithIPv4Index. If this is intentional for comparison purposes, a brief comment explaining the rationale would be helpful for maintainability.

🤖 Prompt for AI Agents
In `@reader_test.go` at line 866, Add a functional test that opens the MMDB with
the WithIPv4Index option and verifies the indexed lookup path returns identical
results to the non-indexed path: call Open("GeoLite2-City.mmdb", WithIPv4Index)
and compare its lookup outputs against Open("GeoLite2-City.mmdb") for the same
test IPs (add this either in TestLookupNetwork or as a new TestIPv4IndexLookup
that iterates existing test cases), ensuring you exercise the functions used for
lookups/decoding to validate correctness; also add brief comments to
BenchmarkDecodeCountryCodeWithStruct, BenchmarkDecodePathCountryCode, and
BenchmarkCityLookupConcurrent explaining why WithIPv4Index is intentionally
omitted or add variants that include it for comparison.

Comment on lines +503 to +512
var (
node uint
prefixLength int
err error
)
if len(r.ipv4Index) > 0 && ip.Is4() {
node, prefixLength, err = r.traverseTreeWithIndex(ip)
} else {
node, prefixLength, err = r.traverseTree(ip, 0, 128)
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor: Consider using r.ipv4Index != nil for the nil check.

While len(r.ipv4Index) > 0 works correctly (returns 0 for nil slices), r.ipv4Index != nil is more idiomatic for checking whether an optional feature is enabled. This also more clearly conveys the intent that we're checking if the index was initialized.

♻️ Suggested change
-if len(r.ipv4Index) > 0 && ip.Is4() {
+if r.ipv4Index != nil && ip.Is4() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var (
node uint
prefixLength int
err error
)
if len(r.ipv4Index) > 0 && ip.Is4() {
node, prefixLength, err = r.traverseTreeWithIndex(ip)
} else {
node, prefixLength, err = r.traverseTree(ip, 0, 128)
}
var (
node uint
prefixLength int
err error
)
if r.ipv4Index != nil && ip.Is4() {
node, prefixLength, err = r.traverseTreeWithIndex(ip)
} else {
node, prefixLength, err = r.traverseTree(ip, 0, 128)
}
🤖 Prompt for AI Agents
In `@reader.go` around lines 503 - 512, Replace the len-based check with a nil
check to clarify intent: change the conditional that decides between
r.traverseTreeWithIndex and r.traverseTree to use "r.ipv4Index != nil" instead
of "len(r.ipv4Index) > 0"; keep the rest of the logic (calling
traverseTreeWithIndex(ip) when the index is present and traverseTree(ip, 0, 128)
otherwise) intact to preserve behavior.

Comment on lines 662 to +670
func (r *Reader) traverseTree(ip netip.Addr, node uint, stopBit int) (uint, int, error) {
startBit := 0
if ip.Is4() {
startBit = r.ipv4StartBitDepth
node = r.ipv4Start
}

return r.traverseTreeFromBit(ip, node, startBit, stopBit)
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

The node parameter appears unused and may cause confusion.

The node parameter passed to traverseTree is always 0 at all call sites and is immediately overwritten when ip.Is4() is true. For IPv6 addresses, the function starts from node 0 regardless of the parameter value.

Consider removing the parameter or documenting why it exists:

♻️ Suggested simplification
-func (r *Reader) traverseTree(ip netip.Addr, node uint, stopBit int) (uint, int, error) {
-	startBit := 0
-	if ip.Is4() {
-		startBit = r.ipv4StartBitDepth
-		node = r.ipv4Start
-	}
+func (r *Reader) traverseTree(ip netip.Addr, stopBit int) (uint, int, error) {
+	startBit := 0
+	node := uint(0)
+	if ip.Is4() {
+		startBit = r.ipv4StartBitDepth
+		node = r.ipv4Start
+	}

 	return r.traverseTreeFromBit(ip, node, startBit, stopBit)
 }

And update call sites accordingly:

-return r.traverseTree(ip, 0, 128)
+return r.traverseTree(ip, 128)
🤖 Prompt for AI Agents
In `@reader.go` around lines 662 - 670, The node parameter in Reader.traverseTree
is unused/overwritten (always 0 at callers and replaced when ip.Is4()), so
remove the node uint parameter from traverseTree and update its body to call
r.traverseTreeFromBit(ip, startNode, startBit, stopBit) using r.ipv4Start (and
r.ipv4StartBitDepth) for IPv4 or 0 for IPv6; also update all call sites to drop
the node argument and adjust any signatures that referenced traverseTree
accordingly while keeping traverseTreeFromBit untouched.

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.

1 participant