diff --git a/benchmarks-runner/build.gradle b/benchmarks-runner/build.gradle index f1d380ae..940fafab 100644 --- a/benchmarks-runner/build.gradle +++ b/benchmarks-runner/build.gradle @@ -64,6 +64,14 @@ task benchmarkAllSets(group: "Benchmark") { dependsOn benchmarkOrderedSetBuilder } +// hash based +task benchmarkAllHashBaseds(group: "Benchmark") { + dependsOn benchmarkHashSet + dependsOn benchmarkHashSetBuilder + dependsOn benchmarkHashMap + dependsOn benchmarkHashMapBuilder +} + // list task benchmarkList(type: JavaExec, group: "Benchmark") { main = 'runners.ListRunnerKt' diff --git a/benchmarks-runner/remoteReferenceBenchmarkResults/hashMap.csv b/benchmarks-runner/remoteReferenceBenchmarkResults/hashMap.csv new file mode 100755 index 00000000..bca2bcb3 --- /dev/null +++ b/benchmarks-runner/remoteReferenceBenchmarkResults/hashMap.csv @@ -0,0 +1,113 @@ +Benchmark,hashCodeType,implementation,size,Score(ns/op),ScoreError(ns/op),AllocRate(B/op) +benchmarks.immutableMap.Get.get,random,hash,1,12.687,0.409,0.000 +benchmarks.immutableMap.Get.get,random,hash,10,14.163,0.065,0.000 +benchmarks.immutableMap.Get.get,random,hash,100,19.201,0.096,0.000 +benchmarks.immutableMap.Get.get,random,hash,1000,30.125,0.672,0.000 +benchmarks.immutableMap.Get.get,random,hash,10000,36.533,0.339,0.000 +benchmarks.immutableMap.Get.get,random,hash,100000,148.799,14.568,0.003 +benchmarks.immutableMap.Get.get,random,hash,1000000,362.524,32.629,0.012 +benchmarks.immutableMap.Get.get,collision,hash,1,12.600,0.441,0.000 +benchmarks.immutableMap.Get.get,collision,hash,10,21.127,0.152,0.000 +benchmarks.immutableMap.Get.get,collision,hash,100,30.171,0.811,21.120 +benchmarks.immutableMap.Get.get,collision,hash,1000,48.375,0.386,0.000 +benchmarks.immutableMap.Get.get,collision,hash,10000,73.060,0.579,0.001 +benchmarks.immutableMap.Get.get,collision,hash,100000,350.547,13.370,0.003 +benchmarks.immutableMap.Get.get,collision,hash,1000000,516.596,41.370,0.002 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,1,49.308,3.455,264.000 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,10,13.637,0.222,50.400 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,100,13.065,0.669,26.640 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,1000,14.377,1.509,24.264 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,10000,12.426,2.529,24.028 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,100000,27.516,5.821,24.003 +benchmarks.immutableMap.Iterate.iterateEntries,random,hash,1000000,35.375,12.123,24.001 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,1,48.387,0.594,264.000 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,10,15.763,0.292,52.000 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,100,14.413,0.817,26.640 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,1000,15.359,1.270,24.264 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,10000,17.133,2.666,24.028 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,100000,35.354,1.575,24.003 +benchmarks.immutableMap.Iterate.iterateEntries,collision,hash,1000000,39.816,6.578,24.001 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,1,42.364,0.416,240.000 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,10,10.595,0.073,26.400 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,100,7.719,0.090,2.640 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,1000,9.109,0.070,0.264 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,10000,9.965,0.131,0.028 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,100000,67.028,5.616,0.003 +benchmarks.immutableMap.Iterate.iterateKeys,random,hash,1000000,62.614,0.736,0.001 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,1,42.219,0.644,240.000 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,10,12.689,0.133,28.000 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,100,11.600,0.445,2.640 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,1000,12.886,0.969,0.264 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,10000,19.323,0.384,0.028 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,100000,91.779,8.530,0.003 +benchmarks.immutableMap.Iterate.iterateKeys,collision,hash,1000000,71.892,3.304,0.001 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,1,44.181,0.505,240.000 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,10,10.732,0.088,26.400 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,100,7.868,0.099,2.640 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,1000,9.128,0.040,0.264 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,10000,9.483,0.056,0.028 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,100000,42.017,13.097,0.003 +benchmarks.immutableMap.Iterate.iterateValues,random,hash,1000000,45.711,2.493,0.001 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,1,43.999,0.339,240.000 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,10,12.971,0.122,28.000 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,100,11.578,0.371,2.640 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,1000,12.876,0.077,0.264 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,10000,16.376,0.072,0.028 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,100000,60.965,30.063,0.003 +benchmarks.immutableMap.Iterate.iterateValues,collision,hash,1000000,57.278,1.884,0.001 +benchmarks.immutableMap.Put.put,random,hash,1,39.103,0.525,104.000 +benchmarks.immutableMap.Put.put,random,hash,10,44.927,0.304,155.200 +benchmarks.immutableMap.Put.put,random,hash,100,69.123,0.618,289.441 +benchmarks.immutableMap.Put.put,random,hash,1000,108.860,8.031,391.569 +benchmarks.immutableMap.Put.put,random,hash,10000,145.405,1.362,502.846 +benchmarks.immutableMap.Put.put,random,hash,100000,313.759,9.722,633.902 +benchmarks.immutableMap.Put.put,random,hash,1000000,556.599,21.212,739.705 +benchmarks.immutableMap.Put.put,collision,hash,1,38.923,0.429,104.000 +benchmarks.immutableMap.Put.put,collision,hash,10,54.369,0.509,164.800 +benchmarks.immutableMap.Put.put,collision,hash,100,73.609,2.045,284.961 +benchmarks.immutableMap.Put.put,collision,hash,1000,110.941,0.565,390.081 +benchmarks.immutableMap.Put.put,collision,hash,10000,180.032,1.269,511.654 +benchmarks.immutableMap.Put.put,collision,hash,100000,396.063,38.803,641.281 +benchmarks.immutableMap.Put.put,collision,hash,1000000,542.765,25.370,727.212 +benchmarks.immutableMap.Put.putAndGet,random,hash,1,51.144,3.666,104.000 +benchmarks.immutableMap.Put.putAndGet,random,hash,10,58.187,0.319,155.200 +benchmarks.immutableMap.Put.putAndGet,random,hash,100,88.796,0.900,289.441 +benchmarks.immutableMap.Put.putAndGet,random,hash,1000,136.051,1.594,391.569 +benchmarks.immutableMap.Put.putAndGet,random,hash,10000,186.196,1.859,502.846 +benchmarks.immutableMap.Put.putAndGet,random,hash,100000,476.945,61.168,633.906 +benchmarks.immutableMap.Put.putAndGet,random,hash,1000000,828.774,65.998,739.716 +benchmarks.immutableMap.Put.putAndGet,collision,hash,1,50.072,2.027,104.000 +benchmarks.immutableMap.Put.putAndGet,collision,hash,10,74.246,2.903,164.801 +benchmarks.immutableMap.Put.putAndGet,collision,hash,100,93.597,0.661,285.121 +benchmarks.immutableMap.Put.putAndGet,collision,hash,1000,149.370,1.578,375.665 +benchmarks.immutableMap.Put.putAndGet,collision,hash,10000,257.691,17.433,511.655 +benchmarks.immutableMap.Put.putAndGet,collision,hash,100000,646.046,49.351,641.283 +benchmarks.immutableMap.Put.putAndGet,collision,hash,1000000,1035.628,75.207,727.212 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,1,81.182,0.847,344.001 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,10,57.620,0.227,181.600 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,100,73.907,0.752,292.081 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,1000,115.808,0.817,391.849 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,10000,160.758,1.445,502.874 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,100000,365.523,19.654,633.905 +benchmarks.immutableMap.Put.putAndIterateKeys,random,hash,1000000,593.923,48.375,739.705 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,1,80.267,1.314,344.001 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,10,76.499,0.784,196.001 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,100,86.237,0.580,287.601 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,1000,123.621,1.427,390.345 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,10000,203.637,3.080,511.682 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,100000,453.465,25.908,641.284 +benchmarks.immutableMap.Put.putAndIterateKeys,collision,hash,1000000,642.868,42.903,727.212 +benchmarks.immutableMap.Remove.remove,random,hash,1,13.086,0.133,0.000 +benchmarks.immutableMap.Remove.remove,random,hash,10,37.665,0.238,111.200 +benchmarks.immutableMap.Remove.remove,random,hash,100,57.017,0.434,228.720 +benchmarks.immutableMap.Remove.remove,random,hash,1000,102.701,3.423,343.313 +benchmarks.immutableMap.Remove.remove,random,hash,10000,154.446,5.535,455.632 +benchmarks.immutableMap.Remove.remove,random,hash,100000,356.230,24.599,576.569 +benchmarks.immutableMap.Remove.remove,random,hash,1000000,584.988,52.811,694.340 +benchmarks.immutableMap.Remove.remove,collision,hash,1,13.048,0.054,0.000 +benchmarks.immutableMap.Remove.remove,collision,hash,10,53.512,1.055,119.200 +benchmarks.immutableMap.Remove.remove,collision,hash,100,75.427,0.954,244.081 +benchmarks.immutableMap.Remove.remove,collision,hash,1000,111.239,0.876,331.569 +benchmarks.immutableMap.Remove.remove,collision,hash,10000,194.205,6.000,464.427 +benchmarks.immutableMap.Remove.remove,collision,hash,100000,486.321,57.263,589.731 +benchmarks.immutableMap.Remove.remove,collision,hash,1000000,699.377,37.001,681.982 diff --git a/benchmarks-runner/remoteReferenceBenchmarkResults/hashMapBuilder.csv b/benchmarks-runner/remoteReferenceBenchmarkResults/hashMapBuilder.csv new file mode 100755 index 00000000..811a2dcb --- /dev/null +++ b/benchmarks-runner/remoteReferenceBenchmarkResults/hashMapBuilder.csv @@ -0,0 +1,113 @@ +Benchmark,hashCodeType,immutablePercentage,implementation,size,Score(ns/op),ScoreError(ns/op),AllocRate(B/op) +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,1,11.227,0.062,0.000 +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,10,14.745,0.837,0.000 +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,100,19.294,0.180,0.000 +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,1000,30.363,0.651,0.000 +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,10000,37.269,0.181,0.000 +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,100000,147.281,30.510,0.003 +benchmarks.immutableMap.builder.Get.get,random,0.0,hash,1000000,385.878,72.407,0.012 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,1,12.590,0.110,0.000 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,10,26.013,0.340,24.000 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,100,34.173,0.746,12.240 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,1000,48.017,0.451,0.000 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,10000,69.709,0.839,0.001 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,100000,257.890,65.597,0.002 +benchmarks.immutableMap.builder.Get.get,collision,0.0,hash,1000000,537.611,9.662,0.002 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,1,59.275,1.081,336.000 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,10,16.565,0.147,66.400 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,100,15.812,0.149,35.440 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,1000,17.009,1.679,32.344 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,10000,12.944,0.174,32.036 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,100000,22.030,5.727,32.004 +benchmarks.immutableMap.builder.Iterate.iterateEntries,random,0.0,hash,1000000,37.173,6.613,32.001 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,1,58.807,0.709,336.000 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,10,18.188,0.248,68.000 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,100,16.829,0.195,35.440 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,1000,17.956,1.112,32.345 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,10000,23.205,1.818,32.036 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,100000,23.026,3.527,32.004 +benchmarks.immutableMap.builder.Iterate.iterateEntries,collision,0.0,hash,1000000,43.226,6.427,32.001 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,1,43.409,0.297,240.000 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,10,11.769,0.091,28.000 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,100,8.483,0.097,2.800 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,1000,10.916,0.051,0.280 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,10000,10.949,0.055,0.030 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,100000,54.272,20.937,0.004 +benchmarks.immutableMap.builder.Iterate.iterateKeys,random,0.0,hash,1000000,77.020,3.824,0.001 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,1,42.830,0.263,240.000 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,10,13.668,0.793,28.000 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,100,10.930,0.377,2.800 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,1000,12.112,0.047,0.280 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,10000,16.528,0.106,0.030 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,100000,84.057,25.291,0.004 +benchmarks.immutableMap.builder.Iterate.iterateKeys,collision,0.0,hash,1000000,76.215,3.073,0.001 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,1,44.795,2.578,240.000 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,10,11.897,0.102,28.000 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,100,9.918,0.094,2.800 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,1000,11.247,0.070,0.280 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,10000,10.745,0.084,0.030 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,100000,19.366,0.751,0.003 +benchmarks.immutableMap.builder.Iterate.iterateValues,random,0.0,hash,1000000,52.717,1.969,0.001 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,1,43.161,0.607,240.000 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,10,14.092,1.013,28.000 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,100,11.233,0.440,2.800 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,1000,11.642,0.065,0.280 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,10000,15.003,0.083,0.030 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,100000,62.575,11.077,0.003 +benchmarks.immutableMap.builder.Iterate.iterateValues,collision,0.0,hash,1000000,61.516,2.531,0.001 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,1,45.211,0.325,136.000 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,10,41.473,2.764,76.800 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,100,50.549,0.546,121.600 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,1000,78.609,7.680,119.801 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,10000,97.579,3.520,93.995 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,100000,199.816,26.197,117.009 +benchmarks.immutableMap.builder.Put.put,random,0.0,hash,1000000,404.044,53.062,119.269 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,1,45.463,0.488,136.000 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,10,51.545,3.122,89.600 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,100,52.544,2.459,119.280 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,1000,81.769,6.092,112.257 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,10000,96.988,4.960,94.690 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,100000,216.680,42.276,123.928 +benchmarks.immutableMap.builder.Put.put,collision,0.0,hash,1000000,473.219,67.075,99.589 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,1,55.908,0.450,136.000 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,10,51.896,5.025,76.800 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,100,71.902,7.117,121.601 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,1000,113.416,12.612,119.785 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,10000,126.132,1.429,93.995 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,100000,350.662,23.391,117.012 +benchmarks.immutableMap.builder.Put.putAndGet,random,0.0,hash,1000000,782.132,61.200,119.280 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,1,56.205,0.496,136.000 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,10,69.053,4.880,80.001 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,100,76.009,5.997,119.281 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,1000,118.479,1.650,97.841 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,10000,165.359,3.773,94.690 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,100000,416.774,74.633,123.929 +benchmarks.immutableMap.builder.Put.putAndGet,collision,0.0,hash,1000000,892.775,59.933,99.589 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,1,88.341,1.509,376.001 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,10,51.926,4.842,104.800 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,100,56.916,1.830,124.400 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,1000,110.620,9.668,120.081 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,10000,112.569,2.473,94.025 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,100000,237.410,46.931,117.012 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,random,0.0,hash,1000000,466.742,57.069,119.269 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,1,90.215,1.693,376.001 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,10,66.164,5.262,119.201 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,100,64.426,5.825,122.081 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,1000,98.437,8.192,112.537 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,10000,112.771,2.154,94.719 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,100000,247.467,19.338,123.931 +benchmarks.immutableMap.builder.Put.putAndIterateKeys,collision,0.0,hash,1000000,525.253,58.651,99.590 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,1,56.618,0.496,136.000 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,10,78.322,4.667,120.801 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,100,98.248,7.528,169.281 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,1000,162.483,13.574,180.193 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,10000,173.233,2.082,144.837 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,100000,443.783,130.628,165.368 +benchmarks.immutableMap.builder.Remove.putAndRemove,random,0.0,hash,1000000,820.546,55.658,179.394 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,1,58.023,0.684,136.000 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,10,93.731,5.649,104.001 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,100,105.967,9.087,161.521 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,1000,171.143,2.344,151.801 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,10000,202.647,6.903,129.752 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,100000,496.678,51.028,169.551 +benchmarks.immutableMap.builder.Remove.putAndRemove,collision,0.0,hash,1000000,1052.503,67.303,139.560 diff --git a/benchmarks-runner/remoteReferenceBenchmarkResults/hashSet.csv b/benchmarks-runner/remoteReferenceBenchmarkResults/hashSet.csv new file mode 100755 index 00000000..4173922f --- /dev/null +++ b/benchmarks-runner/remoteReferenceBenchmarkResults/hashSet.csv @@ -0,0 +1,85 @@ +Benchmark,hashCodeType,implementation,size,Score(ns/op),ScoreError(ns/op),AllocRate(B/op) +benchmarks.immutableSet.Add.add,random,hash,1,42.919,0.518,128.000 +benchmarks.immutableSet.Add.add,random,hash,10,38.136,0.404,101.600 +benchmarks.immutableSet.Add.add,random,hash,100,45.436,1.061,200.560 +benchmarks.immutableSet.Add.add,random,hash,1000,71.262,0.242,297.449 +benchmarks.immutableSet.Add.add,random,hash,10000,111.212,0.880,413.258 +benchmarks.immutableSet.Add.add,random,hash,100000,229.392,14.509,529.010 +benchmarks.immutableSet.Add.add,random,hash,1000000,427.137,27.965,631.105 +benchmarks.immutableSet.Add.add,collision,hash,1,49.256,1.705,160.000 +benchmarks.immutableSet.Add.add,collision,hash,10,39.652,0.217,116.800 +benchmarks.immutableSet.Add.add,collision,hash,100,47.353,0.445,200.720 +benchmarks.immutableSet.Add.add,collision,hash,1000,74.162,0.543,296.097 +benchmarks.immutableSet.Add.add,collision,hash,10000,127.752,3.074,426.245 +benchmarks.immutableSet.Add.add,collision,hash,100000,257.842,35.355,535.286 +benchmarks.immutableSet.Add.add,collision,hash,1000000,472.430,26.604,630.541 +benchmarks.immutableSet.Add.addAndContains,random,hash,1,51.643,0.372,128.000 +benchmarks.immutableSet.Add.addAndContains,random,hash,10,47.718,0.933,101.600 +benchmarks.immutableSet.Add.addAndContains,random,hash,100,62.275,0.580,200.561 +benchmarks.immutableSet.Add.addAndContains,random,hash,1000,98.750,0.700,297.449 +benchmarks.immutableSet.Add.addAndContains,random,hash,10000,149.436,1.140,413.258 +benchmarks.immutableSet.Add.addAndContains,random,hash,100000,337.025,33.973,529.011 +benchmarks.immutableSet.Add.addAndContains,random,hash,1000000,645.801,39.799,631.105 +benchmarks.immutableSet.Add.addAndContains,collision,hash,1,51.593,0.295,128.000 +benchmarks.immutableSet.Add.addAndContains,collision,hash,10,61.418,3.831,116.801 +benchmarks.immutableSet.Add.addAndContains,collision,hash,100,72.772,0.793,200.401 +benchmarks.immutableSet.Add.addAndContains,collision,hash,1000,118.744,1.524,296.097 +benchmarks.immutableSet.Add.addAndContains,collision,hash,10000,205.276,9.987,426.246 +benchmarks.immutableSet.Add.addAndContains,collision,hash,100000,410.519,41.929,535.287 +benchmarks.immutableSet.Add.addAndContains,collision,hash,1000000,842.058,72.060,630.541 +benchmarks.immutableSet.Add.addAndIterate,random,hash,1,70.030,0.434,256.001 +benchmarks.immutableSet.Add.addAndIterate,random,hash,10,58.455,4.384,118.400 +benchmarks.immutableSet.Add.addAndIterate,random,hash,100,65.929,0.436,202.801 +benchmarks.immutableSet.Add.addAndIterate,random,hash,1000,95.714,0.923,297.793 +benchmarks.immutableSet.Add.addAndIterate,random,hash,10000,143.977,2.999,413.297 +benchmarks.immutableSet.Add.addAndIterate,random,hash,100000,317.886,25.813,529.015 +benchmarks.immutableSet.Add.addAndIterate,random,hash,1000000,489.440,19.214,631.106 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,1,70.109,0.608,256.001 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,10,69.251,4.699,133.601 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,100,75.374,0.518,202.641 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,1000,108.063,0.831,296.353 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,10000,181.545,2.602,426.276 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,100000,410.226,43.037,535.291 +benchmarks.immutableSet.Add.addAndIterate,collision,hash,1000000,548.803,16.078,630.541 +benchmarks.immutableSet.Contains.contains,random,hash,1,11.802,0.102,0.000 +benchmarks.immutableSet.Contains.contains,random,hash,10,12.907,0.253,0.000 +benchmarks.immutableSet.Contains.contains,random,hash,100,16.492,0.098,0.000 +benchmarks.immutableSet.Contains.contains,random,hash,1000,26.508,0.581,0.000 +benchmarks.immutableSet.Contains.contains,random,hash,10000,33.541,0.174,0.000 +benchmarks.immutableSet.Contains.contains,random,hash,100000,110.097,49.262,0.001 +benchmarks.immutableSet.Contains.contains,random,hash,1000000,276.657,57.550,0.002 +benchmarks.immutableSet.Contains.contains,collision,hash,1,11.806,0.062,0.000 +benchmarks.immutableSet.Contains.contains,collision,hash,10,21.834,0.295,0.000 +benchmarks.immutableSet.Contains.contains,collision,hash,100,22.378,0.190,0.000 +benchmarks.immutableSet.Contains.contains,collision,hash,1000,46.623,0.315,0.000 +benchmarks.immutableSet.Contains.contains,collision,hash,10000,61.407,0.803,0.001 +benchmarks.immutableSet.Contains.contains,collision,hash,100000,171.766,50.651,0.002 +benchmarks.immutableSet.Contains.contains,collision,hash,1000000,424.307,19.774,0.002 +benchmarks.immutableSet.Iterate.iterate,random,hash,1,25.262,0.240,96.000 +benchmarks.immutableSet.Iterate.iterate,random,hash,10,16.938,1.276,16.800 +benchmarks.immutableSet.Iterate.iterate,random,hash,100,18.029,0.195,2.240 +benchmarks.immutableSet.Iterate.iterate,random,hash,1000,17.131,0.119,0.344 +benchmarks.immutableSet.Iterate.iterate,random,hash,10000,18.576,0.243,0.037 +benchmarks.immutableSet.Iterate.iterate,random,hash,100000,106.533,19.249,0.006 +benchmarks.immutableSet.Iterate.iterate,random,hash,1000000,71.621,2.689,0.001 +benchmarks.immutableSet.Iterate.iterate,collision,hash,1,25.126,0.220,96.000 +benchmarks.immutableSet.Iterate.iterate,collision,hash,10,21.160,0.688,16.800 +benchmarks.immutableSet.Iterate.iterate,collision,hash,100,19.825,0.158,2.240 +benchmarks.immutableSet.Iterate.iterate,collision,hash,1000,17.475,0.112,0.224 +benchmarks.immutableSet.Iterate.iterate,collision,hash,10000,27.629,0.548,0.028 +benchmarks.immutableSet.Iterate.iterate,collision,hash,100000,103.177,12.291,0.005 +benchmarks.immutableSet.Iterate.iterate,collision,hash,1000000,83.294,1.881,0.001 +benchmarks.immutableSet.Remove.remove,random,hash,1,13.666,0.100,0.000 +benchmarks.immutableSet.Remove.remove,random,hash,10,40.843,3.131,80.800 +benchmarks.immutableSet.Remove.remove,random,hash,100,56.763,0.524,193.680 +benchmarks.immutableSet.Remove.remove,random,hash,1000,96.726,0.917,292.569 +benchmarks.immutableSet.Remove.remove,random,hash,10000,131.345,1.805,409.518 +benchmarks.immutableSet.Remove.remove,random,hash,100000,343.823,26.053,525.742 +benchmarks.immutableSet.Remove.remove,random,hash,1000000,488.559,39.103,628.058 +benchmarks.immutableSet.Remove.remove,collision,hash,1,12.232,0.036,0.000 +benchmarks.immutableSet.Remove.remove,collision,hash,10,46.446,0.485,98.400 +benchmarks.immutableSet.Remove.remove,collision,hash,100,63.146,0.418,210.161 +benchmarks.immutableSet.Remove.remove,collision,hash,1000,91.473,0.929,292.369 +benchmarks.immutableSet.Remove.remove,collision,hash,10000,153.936,4.462,424.233 +benchmarks.immutableSet.Remove.remove,collision,hash,100000,385.582,33.094,536.112 +benchmarks.immutableSet.Remove.remove,collision,hash,1000000,508.321,20.909,627.485 diff --git a/benchmarks-runner/remoteReferenceBenchmarkResults/hashSetBuilder.csv b/benchmarks-runner/remoteReferenceBenchmarkResults/hashSetBuilder.csv new file mode 100755 index 00000000..16c51e8b --- /dev/null +++ b/benchmarks-runner/remoteReferenceBenchmarkResults/hashSetBuilder.csv @@ -0,0 +1,85 @@ +Benchmark,hashCodeType,immutablePercentage,implementation,size,Score(ns/op),ScoreError(ns/op),AllocRate(B/op) +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,1,48.466,1.118,152.000 +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,10,38.434,3.169,55.200 +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,100,40.896,2.544,55.600 +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,1000,59.709,1.154,58.136 +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,10000,66.502,1.296,48.730 +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,100000,160.220,10.894,54.235 +benchmarks.immutableSet.builder.Add.add,random,0.0,hash,1000000,347.730,59.378,57.691 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,1,57.489,0.448,184.000 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,10,34.156,2.619,52.800 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,100,37.113,7.277,54.080 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,1000,49.876,0.649,45.752 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,10000,69.193,0.620,44.685 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,100000,147.794,16.585,54.510 +benchmarks.immutableSet.builder.Add.add,collision,0.0,hash,1000000,383.890,42.485,46.310 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,1,67.765,0.717,184.001 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,10,49.270,4.594,55.200 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,100,57.988,0.817,55.600 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,1000,88.558,1.171,58.137 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,10000,98.010,3.187,48.730 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,100000,265.618,41.186,54.236 +benchmarks.immutableSet.builder.Add.addAndContains,random,0.0,hash,1000000,613.840,73.179,57.691 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,1,67.173,0.607,184.001 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,10,53.459,1.052,52.800 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,100,58.335,0.550,54.080 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,1000,88.320,1.385,45.753 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,10000,123.616,3.080,44.686 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,100000,256.832,32.779,54.510 +benchmarks.immutableSet.builder.Add.addAndContains,collision,0.0,hash,1000000,757.135,74.946,46.310 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,1,84.975,1.178,288.001 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,10,57.007,4.943,73.601 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,100,53.292,3.827,58.000 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,1000,74.153,0.979,58.497 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,10000,87.234,2.698,48.771 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,100000,230.779,23.714,54.240 +benchmarks.immutableSet.builder.Add.addAndIterate,random,0.0,hash,1000000,430.263,37.438,57.692 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,1,89.541,0.798,320.001 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,10,60.954,3.809,71.200 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,100,60.485,0.743,56.481 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,1000,75.615,0.987,45.993 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,10000,105.065,1.959,44.718 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,100000,219.511,73.129,54.514 +benchmarks.immutableSet.builder.Add.addAndIterate,collision,0.0,hash,1000000,525.629,65.155,46.310 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,1,11.855,0.099,0.000 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,10,12.845,0.065,0.000 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,100,16.443,0.035,0.000 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,1000,26.553,1.151,0.000 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,10000,34.358,0.175,0.000 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,100000,83.487,9.162,0.001 +benchmarks.immutableSet.builder.Contains.contains,random,0.0,hash,1000000,295.160,28.472,0.002 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,1,11.868,0.120,0.000 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,10,21.981,0.726,0.000 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,100,24.734,2.984,0.000 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,1000,46.671,0.220,0.000 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,10000,60.642,0.331,0.000 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,100000,180.726,61.619,0.001 +benchmarks.immutableSet.builder.Contains.contains,collision,0.0,hash,1000000,478.399,31.195,0.002 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,1,34.203,0.553,136.000 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,10,17.783,1.134,18.400 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,100,19.031,0.139,2.400 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,1000,18.902,3.253,0.360 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,10000,18.973,0.188,0.039 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,100000,86.836,25.223,0.006 +benchmarks.immutableSet.builder.Iterate.iterate,random,0.0,hash,1000000,109.149,4.279,0.001 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,1,33.913,0.327,136.000 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,10,22.222,0.259,18.400 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,100,21.083,0.118,2.400 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,1000,18.243,0.258,0.240 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,10000,24.225,0.122,0.030 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,100000,81.135,21.929,0.004 +benchmarks.immutableSet.builder.Iterate.iterate,collision,0.0,hash,1000000,138.669,5.430,0.001 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,1,71.980,0.898,184.001 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,10,80.315,5.263,86.401 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,100,82.471,4.709,97.681 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,1000,128.841,2.175,105.297 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,10000,150.243,10.089,88.305 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,100000,320.548,26.662,97.282 +benchmarks.immutableSet.builder.Remove.addAndRemove,random,0.0,hash,1000000,745.975,39.818,104.690 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,1,63.052,0.485,152.001 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,10,75.239,3.301,75.201 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,100,86.277,7.538,93.121 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,1000,118.664,2.864,80.449 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,10000,151.925,8.711,76.624 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,100000,368.270,22.044,95.848 +benchmarks.immutableSet.builder.Remove.addAndRemove,collision,0.0,hash,1000000,780.477,65.546,81.494 diff --git a/benchmarks-runner/src/main/kotlin/contants.kt b/benchmarks-runner/src/main/kotlin/contants.kt index 3e4dc33a..926e5998 100644 --- a/benchmarks-runner/src/main/kotlin/contants.kt +++ b/benchmarks-runner/src/main/kotlin/contants.kt @@ -43,10 +43,10 @@ val jvmArgs = arrayOf("-Xms2048m", "-Xmx2048m") const val forks = 1 const val warmupIterations = 10 -const val measurementIterations = 20 -val warmupTime = TimeValue.milliseconds(500)!! -val measurementTime = TimeValue.milliseconds(1000)!! +const val measurementIterations = 10 +val warmupTime = TimeValue.milliseconds(200)!! +val measurementTime = TimeValue.milliseconds(200)!! val sizeParamValues = arrayOf("1", "10", "100", "1000", "10000", "100000", "1000000") -val hashCodeTypeParamValues = arrayOf("ascending", "random", "collision", "nonExisting") -val immutablePercentageParamValues = arrayOf("0.0", "20.0", "50.0", "90.0") +val hashCodeTypeParamValues = arrayOf(/*"ascending", */"random", "collision"/*, "nonExisting"*/) +val immutablePercentageParamValues = arrayOf("0.0"/*, "20.0", "50.0", "90.0"*/) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt index e3bd0906..9ff80f32 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt @@ -79,7 +79,7 @@ internal abstract class PersistentHashMapBuilderBaseIterator(private va val nodeIndex = node.nodeIndex(keyPositionMask) val targetNode = node.nodeAtIndex(nodeIndex) - if (shift == MAX_SHIFT) { // collision + if (targetNode.isCollision()) { path[pathIndex].reset(node.buffer, node.buffer.size, 0) while (path[pathIndex].currentKey() != key) { path[pathIndex].moveToNextKey() diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt index 6da0237c..73ad61fb 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt @@ -105,7 +105,11 @@ internal abstract class PersistentHashMapBaseIterator(node: TrieNode(node: TrieNode( internal var buffer: Array = buffer private set + @Suppress("NOTHING_TO_INLINE") + internal inline fun isCollision(): Boolean { + return dataMap == 0 && nodeMap == 0 && buffer.isNotEmpty() + } + /** Returns number of entries stored in this trie node (not counting subnodes) */ @UseExperimental(ExperimentalStdlibApi::class) internal fun entryCount(): Int = dataMap.countOneBits() @@ -190,52 +194,51 @@ internal class TrieNode( return TrieNode(dataMap, nodeMap, newBuffer, owner) } - private fun bufferMoveEntryToNode(keyIndex: Int, positionMask: Int, newKeyHash: Int, - newKey: K, newValue: V, shift: Int, owner: MutabilityOwnership?): Array { + private fun moveEntryToNode(keyIndex: Int, positionMask: Int, newKeyHash: Int, + newKey: K, newValue: V, shift: Int, owner: MutabilityOwnership?): TrieNode { val storedKey = keyAtIndex(keyIndex) val storedKeyHash = storedKey.hashCode() val storedValue = valueAtKeyIndex(keyIndex) - val newNode = makeNode(storedKeyHash, storedKey, storedValue, - newKeyHash, newKey, newValue, shift + LOG_MAX_BRANCHING_FACTOR, owner) - val nodeIndex = nodeIndex(positionMask) + 1 // place where to insert new node in the current buffer + val newNode: TrieNode + if (storedKeyHash == newKeyHash) { + newNode = makeCollisionNode(newKey, newValue, storedKey, storedValue, owner) + if (buffer.size == ENTRY_SIZE) { + return newNode + } + } else { + newNode = makeNode(storedKeyHash, storedKey, storedValue, + newKeyHash, newKey, newValue, shift + LOG_MAX_BRANCHING_FACTOR, owner) + } - return buffer.replaceEntryWithNode(keyIndex, nodeIndex, newNode) - } + val nodeIndex = nodeIndex(positionMask) + 1 // place where to insert new node in the current buffer + val newBuffer = buffer.replaceEntryWithNode(keyIndex, nodeIndex, newNode) + val newDataMap = dataMap xor positionMask + val newNodeMap = nodeMap or positionMask - private fun moveEntryToNode(keyIndex: Int, positionMask: Int, newKeyHash: Int, - newKey: K, newValue: V, shift: Int): TrieNode { -// assert(hasEntryAt(positionMask)) -// assert(!hasNodeAt(positionMask)) + if (ownedBy != null && ownedBy === owner) { + buffer = newBuffer + dataMap = newDataMap + nodeMap = newNodeMap + return this + } - val newBuffer = bufferMoveEntryToNode(keyIndex, positionMask, newKeyHash, newKey, newValue, shift, null) - return TrieNode(dataMap xor positionMask, nodeMap or positionMask, newBuffer) + return TrieNode(newDataMap, newNodeMap, newBuffer, owner) } - private fun mutableMoveEntryToNode(keyIndex: Int, positionMask: Int, newKeyHash: Int, - newKey: K, newValue: V, shift: Int, owner: MutabilityOwnership): TrieNode { -// assert(hasEntryAt(positionMask)) -// assert(!hasNodeAt(positionMask)) + private fun makeCollisionNode(key1: K, value1: V, key2: K, value2: V, owner: MutabilityOwnership?): TrieNode { + return makeCollisionNode(arrayOf(key1, value1, key2, value2), owner) + } - if (ownedBy === owner) { - buffer = bufferMoveEntryToNode(keyIndex, positionMask, newKeyHash, newKey, newValue, shift, owner) - dataMap = dataMap xor positionMask - nodeMap = nodeMap or positionMask - return this - } - val newBuffer = bufferMoveEntryToNode(keyIndex, positionMask, newKeyHash, newKey, newValue, shift, owner) - return TrieNode(dataMap xor positionMask, nodeMap or positionMask, newBuffer, owner) + private fun makeCollisionNode(buffer: Array, owner: MutabilityOwnership?): TrieNode { + return TrieNode(0, 0, buffer, owner) } /** Creates a new TrieNode for holding two given key value entries */ private fun makeNode(keyHash1: Int, key1: K, value1: V, keyHash2: Int, key2: K, value2: V, shift: Int, owner: MutabilityOwnership?): TrieNode { - if (shift > MAX_SHIFT) { -// assert(key1 != key2) - // when two key hashes are entirely equal: the last level subtrie node stores them just as unordered list - return TrieNode(0, 0, arrayOf(key1, value1, key2, value2), owner) - } +// assert(keyHash1 != keyHash2) val setBit1 = indexSegment(keyHash1, shift) val setBit2 = indexSegment(keyHash2, shift) @@ -253,6 +256,23 @@ internal class TrieNode( return TrieNode(0, 1 shl setBit1, arrayOf(node), owner) } + /** Creates a new TrieNode for holding the given key value entry and the given collision node */ + private fun makeNode(collisionHash: Int, collisionNode: TrieNode, + keyHash: Int, key: K, value: V, shift: Int, owner: MutabilityOwnership?): TrieNode { +// assert(collisionHash != keyHash) + + val collisionBits = indexSegment(collisionHash, shift) + val keyBits = indexSegment(keyHash, shift) + + if (collisionBits != keyBits) { + val nodeBuffer = arrayOf(key, value, collisionNode) + return TrieNode(1 shl keyBits, (1 shl collisionBits), nodeBuffer, owner) + } + + val node = makeNode(collisionHash, collisionNode, keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, owner) + return TrieNode(0, 1 shl collisionBits, arrayOf(node), owner) + } + private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode? { // assert(hasEntryAt(positionMask)) if (buffer.size == ENTRY_SIZE) return null @@ -280,7 +300,7 @@ internal class TrieNode( if (buffer.size == ENTRY_SIZE) return null val newBuffer = buffer.removeEntryAtIndex(i) - return TrieNode(0, 0, newBuffer) + return makeCollisionNode(newBuffer, null) } private fun mutableCollisionRemoveEntryAtIndex(i: Int, mutator: PersistentHashMapBuilder): TrieNode? { @@ -293,7 +313,7 @@ internal class TrieNode( return this } val newBuffer = buffer.removeEntryAtIndex(i) - return TrieNode(0, 0, newBuffer, mutator.ownership) + return makeCollisionNode(newBuffer, mutator.ownership) } private fun removeNodeAtIndex(nodeIndex: Int, positionMask: Int): TrieNode? { @@ -317,14 +337,24 @@ internal class TrieNode( return TrieNode(dataMap, nodeMap xor positionMask, newBuffer, owner) } - private fun collisionContainsKey(key: K): Boolean { + private fun collisionContainsKey(keyHash: Int, key: K): Boolean { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return false + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == buffer[i]) return true } return false } - private fun collisionGet(key: K): V? { + private fun collisionGet(keyHash: Int, key: K): V? { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return null + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { return valueAtKeyIndex(i) @@ -333,7 +363,12 @@ internal class TrieNode( return null } - private fun collisionPut(key: K, value: V): ModificationResult? { + private fun collisionPut(keyHash: Int, key: K, value: V, shift: Int): ModificationResult? { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return makeNode(collisionHash, this, keyHash, key, value, shift, null).asInsertResult() + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { if (value === valueAtKeyIndex(i)) { @@ -341,14 +376,20 @@ internal class TrieNode( } val newBuffer = buffer.copyOf() newBuffer[i + 1] = value - return TrieNode(0, 0, newBuffer).asUpdateResult() + return makeCollisionNode(newBuffer, null).asUpdateResult() } } val newBuffer = buffer.insertEntryAtIndex(0, key, value) - return TrieNode(0, 0, newBuffer).asInsertResult() + return makeCollisionNode(newBuffer, null).asInsertResult() } - private fun mutableCollisionPut(key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode { + private fun mutableCollisionPut(keyHash: Int, key: K, value: V, shift: Int, mutator: PersistentHashMapBuilder): TrieNode { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + mutator.size++ + return makeNode(collisionHash, this, keyHash, key, value, shift, mutator.ownership) + } + // Check if there is an entry with the specified key. for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { // found entry with the specified key @@ -365,16 +406,21 @@ internal class TrieNode( // Create new node with updated entry value. val newBuffer = buffer.copyOf() newBuffer[i + 1] = value - return TrieNode(0, 0, newBuffer, mutator.ownership) + return makeCollisionNode(newBuffer, mutator.ownership) } } // Create new collision node with the specified entry added to it. mutator.size++ val newBuffer = buffer.insertEntryAtIndex(0, key, value) - return TrieNode(0, 0, newBuffer, mutator.ownership) + return makeCollisionNode(newBuffer, mutator.ownership) } - private fun collisionRemove(key: K): TrieNode? { + private fun collisionRemove(keyHash: Int, key: K): TrieNode? { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return this + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { return collisionRemoveEntryAtIndex(i) @@ -383,7 +429,12 @@ internal class TrieNode( return this } - private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder): TrieNode? { + private fun mutableCollisionRemove(keyHash: Int, key: K, mutator: PersistentHashMapBuilder): TrieNode? { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return this + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { return mutableCollisionRemoveEntryAtIndex(i, mutator) @@ -392,7 +443,12 @@ internal class TrieNode( return this } - private fun collisionRemove(key: K, value: V): TrieNode? { + private fun collisionRemove(keyHash: Int, key: K, value: V): TrieNode? { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return this + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) { return collisionRemoveEntryAtIndex(i) @@ -401,7 +457,12 @@ internal class TrieNode( return this } - private fun mutableCollisionRemove(key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode? { + private fun mutableCollisionRemove(keyHash: Int, key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode? { + val collisionHash = keyAtIndex(0).hashCode() + if (keyHash != collisionHash) { + return this + } + for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) { return mutableCollisionRemoveEntryAtIndex(i, mutator) @@ -418,12 +479,13 @@ internal class TrieNode( } if (hasNodeAt(keyPositionMask)) { // key is in node val targetNode = nodeAtIndex(nodeIndex(keyPositionMask)) - if (shift == MAX_SHIFT) { - return targetNode.collisionContainsKey(key) - } return targetNode.containsKey(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR) } + if (isCollision()) { + return collisionContainsKey(keyHash, key) + } + // key is absent return false } @@ -441,12 +503,13 @@ internal class TrieNode( } if (hasNodeAt(keyPositionMask)) { // key is in node val targetNode = nodeAtIndex(nodeIndex(keyPositionMask)) - if (shift == MAX_SHIFT) { - return targetNode.collisionGet(key) - } return targetNode.get(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR) } + if (isCollision()) { + return collisionGet(keyHash, key) + } + // key is absent return null } @@ -462,20 +525,20 @@ internal class TrieNode( return updateValueAtIndex(keyIndex, value).asUpdateResult() } - return moveEntryToNode(keyIndex, keyPositionMask, keyHash, key, value, shift).asInsertResult() + return moveEntryToNode(keyIndex, keyPositionMask, keyHash, key, value, shift, null).asInsertResult() } if (hasNodeAt(keyPositionMask)) { // key is in node val nodeIndex = nodeIndex(keyPositionMask) val targetNode = nodeAtIndex(nodeIndex) - val putResult = if (shift == MAX_SHIFT) { - targetNode.collisionPut(key, value) ?: return null - } else { - targetNode.put(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) ?: return null - } + val putResult = targetNode.put(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) ?: return null return putResult.replaceNode { node -> updateNodeAtIndex(nodeIndex, node) } } + if (isCollision()) { + return collisionPut(keyHash, key, value, shift) + } + // no entry at this key hash segment return insertEntryAt(keyPositionMask, key, value).asInsertResult() } @@ -495,23 +558,23 @@ internal class TrieNode( return mutableUpdateValueAtIndex(keyIndex, value, mutator) } mutator.size++ - return mutableMoveEntryToNode(keyIndex, keyPositionMask, keyHash, key, value, shift, mutator.ownership) + return moveEntryToNode(keyIndex, keyPositionMask, keyHash, key, value, shift, mutator.ownership) } if (hasNodeAt(keyPositionMask)) { // key is in node val nodeIndex = nodeIndex(keyPositionMask) val targetNode = nodeAtIndex(nodeIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.mutableCollisionPut(key, value, mutator) - } else { - targetNode.mutablePut(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) - } + val newNode = targetNode.mutablePut(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) if (targetNode === newNode) { return this } return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership) } + if (isCollision()) { + return mutableCollisionPut(keyHash, key, value, shift, mutator) + } + // key is absent mutator.size++ return mutableInsertEntryAt(keyPositionMask, key, value, mutator.ownership) @@ -532,11 +595,7 @@ internal class TrieNode( val nodeIndex = nodeIndex(keyPositionMask) val targetNode = nodeAtIndex(nodeIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.collisionRemove(key) - } else { - targetNode.remove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR) - } + val newNode = targetNode.remove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR) return when { targetNode === newNode -> this newNode == null -> removeNodeAtIndex(nodeIndex, keyPositionMask) @@ -544,6 +603,10 @@ internal class TrieNode( } } + if (isCollision()) { + return collisionRemove(keyHash, key) + } + // key is absent return this } @@ -563,11 +626,7 @@ internal class TrieNode( val nodeIndex = nodeIndex(keyPositionMask) val targetNode = nodeAtIndex(nodeIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.mutableCollisionRemove(key, mutator) - } else { - targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator) - } + val newNode = targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator) return when { targetNode === newNode -> this newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, keyPositionMask, mutator.ownership) @@ -575,6 +634,10 @@ internal class TrieNode( } } + if (isCollision()) { + return mutableCollisionRemove(keyHash, key, mutator) + } + // key is absent return this } @@ -594,11 +657,7 @@ internal class TrieNode( val nodeIndex = nodeIndex(keyPositionMask) val targetNode = nodeAtIndex(nodeIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.collisionRemove(key, value) - } else { - targetNode.remove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) - } + val newNode = targetNode.remove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) return when { targetNode === newNode -> this newNode == null -> removeNodeAtIndex(nodeIndex, keyPositionMask) @@ -606,6 +665,10 @@ internal class TrieNode( } } + if (isCollision()) { + return collisionRemove(keyHash, key, value) + } + // key is absent return this } @@ -625,11 +688,7 @@ internal class TrieNode( val nodeIndex = nodeIndex(keyPositionMask) val targetNode = nodeAtIndex(nodeIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.mutableCollisionRemove(key, value, mutator) - } else { - targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) - } + val newNode = targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) return when { targetNode === newNode -> this newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, keyPositionMask, mutator.ownership) @@ -637,6 +696,10 @@ internal class TrieNode( } } + if (isCollision()) { + return mutableCollisionRemove(keyHash, key, value, mutator) + } + // key is absent return this } diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt index dd08a99a..3fcd7f3e 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt @@ -38,7 +38,7 @@ internal class PersistentHashSetMutableIterator(private val builder: Persiste } private fun resetPath(hashCode: Int, node: TrieNode<*>, element: E, pathIndex: Int) { - if (isCollision(node)) { + if (node.isCollision()) { val index = node.buffer.indexOf(element) assert(index != -1) path[pathIndex].reset(node.buffer, index) @@ -61,10 +61,6 @@ internal class PersistentHashSetMutableIterator(private val builder: Persiste } } - private fun isCollision(node: TrieNode<*>): Boolean { - return node.bitmap == 0 - } - private fun checkNextWasInvoked() { if (!nextWasInvoked) throw IllegalStateException() diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 9a347410..1eb5562f 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -11,7 +11,6 @@ import kotlinx.collections.immutable.internal.MutabilityOwnership internal const val MAX_BRANCHING_FACTOR = 32 internal const val LOG_MAX_BRANCHING_FACTOR = 5 internal const val MAX_BRANCHING_FACTOR_MINUS_ONE = MAX_BRANCHING_FACTOR - 1 -internal const val MAX_SHIFT = 30 /** * Gets trie index segment of the specified [index] at the level specified by [shift]. @@ -109,38 +108,43 @@ internal class TrieNode( return TrieNode(bitmap, newBuffer, owner) } - private fun makeNodeAtIndex(elementIndex: Int, newElementHash: Int, newElement: E, - shift: Int, owner: MutabilityOwnership?): TrieNode { + private fun moveElementToNode(elementIndex: Int, newElementHash: Int, newElement: E, + shift: Int, owner: MutabilityOwnership?): TrieNode { val storedElement = elementAtIndex(elementIndex) - return makeNode(storedElement.hashCode(), storedElement, - newElementHash, newElement, shift + LOG_MAX_BRANCHING_FACTOR, owner) - } + val storedElementHash = storedElement.hashCode() - private fun moveElementToNode(elementIndex: Int, newElementHash: Int, newElement: E, - shift: Int): TrieNode { - val newBuffer = buffer.copyOf() - newBuffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, null) - return TrieNode(bitmap, newBuffer) - } + val newNode: TrieNode - private fun mutableMoveElementToNode(elementIndex: Int, newElementHash: Int, newElement: E, - shift: Int, owner: MutabilityOwnership): TrieNode { - if (ownedBy === owner) { - buffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, owner) + if (storedElementHash == newElementHash) { + newNode = makeCollisionNode(storedElement, newElement, owner) + if (buffer.size == 1) { + return newNode + } + } else { + newNode = makeNode(storedElementHash, storedElement, + newElementHash, newElement, shift + LOG_MAX_BRANCHING_FACTOR, owner) + } + + if (ownedBy != null && ownedBy === owner) { + buffer[elementIndex] = newNode return this } val newBuffer = buffer.copyOf() - newBuffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, owner) + newBuffer[elementIndex] = newNode return TrieNode(bitmap, newBuffer, owner) } + private fun makeCollisionNode(element1: E, element2: E, owner: MutabilityOwnership?): TrieNode { + return makeCollisionNode(arrayOf(element1, element2), owner) + } + + private fun makeCollisionNode(buffer: Array, owner: MutabilityOwnership?): TrieNode { + return TrieNode(0, buffer, owner) + } + private fun makeNode(elementHash1: Int, element1: E, elementHash2: Int, element2: E, shift: Int, owner: MutabilityOwnership?): TrieNode { - if (shift > MAX_SHIFT) { -// assert(element1 != element2) - // when two element hashes are entirely equal: the last level subtrie node stores them just as unordered list - return TrieNode(0, arrayOf(element1, element2), owner) - } +// assert(elementHash1 != elementHash2) val setBit1 = indexSegment(elementHash1, shift) val setBit2 = indexSegment(elementHash2, shift) @@ -158,6 +162,25 @@ internal class TrieNode( return TrieNode(1 shl setBit1, arrayOf(node), owner) } + private fun makeNode(collisionHash: Int, elementHash: Int, element: E, shift: Int, owner: MutabilityOwnership?): TrieNode { +// assert(elementHash != collisionHash) + + val collisionBits = indexSegment(collisionHash, shift) + val elementBits = indexSegment(elementHash, shift) + + if (collisionBits != elementBits) { + val nodeBuffer = if (collisionBits < elementBits) { + arrayOf(this, element) + } else { + arrayOf(element, this) + } + return TrieNode((1 shl collisionBits) or (1 shl elementBits), nodeBuffer, owner) + } + + val node = makeNode(collisionHash, elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, owner) + return TrieNode(1 shl collisionBits, arrayOf(node), owner) + } + private fun removeCellAtIndex(cellIndex: Int, positionMask: Int): TrieNode? { // assert(!hasNoCellAt(positionMask)) @@ -180,75 +203,89 @@ internal class TrieNode( return TrieNode(bitmap xor positionMask, newBuffer, owner) } - private fun collisionRemoveElementAtIndex(i: Int): TrieNode? { - if (buffer.size == 1) return null - - val newBuffer = buffer.removeCellAtIndex(i) - return TrieNode(0, newBuffer) + private fun collisionContains(elementHash: Int, element: E): Boolean { + if (elementHash != buffer[0].hashCode()) { + return false + } + return buffer.contains(element) } - private fun mutableCollisionRemoveElementAtIndex(i: Int, owner: MutabilityOwnership): TrieNode? { - if (buffer.size == 1) return null - - if (ownedBy === owner) { - buffer = buffer.removeCellAtIndex(i) + private fun collisionAdd(elementHash: Int, element: E, shift: Int): TrieNode { + val collisionHash = buffer[0].hashCode() + if (elementHash != collisionHash) { + return makeNode(collisionHash, elementHash, element, shift, null) + } + if (buffer.contains(element)) { return this } - val newBuffer = buffer.removeCellAtIndex(i) - return TrieNode(0, newBuffer, owner) + return makeCollisionNode(buffer + element, null) } - private fun collisionContainsElement(element: E): Boolean { - return buffer.contains(element) - } - - private fun collisionAdd(element: E): TrieNode { - if (collisionContainsElement(element)) return this - val newBuffer = buffer.addElementAtIndex(0, element) - return TrieNode(0, newBuffer) - } + private fun collisionMutableAdd(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): TrieNode { + val collisionHash = buffer[0].hashCode() + if (collisionHash != elementHash) { + mutator.size++ + return makeNode(collisionHash, elementHash, element, shift, mutator.ownership) + } + if (buffer.contains(element)) { + return this + } - private fun mutableCollisionAdd(element: E, mutator: PersistentHashSetBuilder<*>): TrieNode { - if (collisionContainsElement(element)) return this mutator.size++ + if (ownedBy === mutator.ownership) { - buffer = buffer.addElementAtIndex(0, element) + buffer += element return this } - val newBuffer = buffer.addElementAtIndex(0, element) - return TrieNode(0, newBuffer, mutator.ownership) + return makeCollisionNode(buffer + element, mutator.ownership) } - private fun collisionRemove(element: E): TrieNode? { - val index = buffer.indexOf(element) - if (index != -1) { - return collisionRemoveElementAtIndex(index) + private fun collisionRemove(elementHash: Int, element: E): TrieNode? { + if (buffer[0].hashCode() != elementHash) { + return this } - return this + val index = buffer.indexOf(element) + if (index == -1) return this + if (buffer.size == 1) return null + return makeCollisionNode(buffer.removeCellAtIndex(index), null) } - private fun mutableCollisionRemove(element: E, mutator: PersistentHashSetBuilder<*>): TrieNode? { + private fun collisionMutableRemove(elementHash: Int, element: E, mutator: PersistentHashSetBuilder<*>): TrieNode? { + if (buffer[0].hashCode() != elementHash) { + return this + } + val index = buffer.indexOf(element) - if (index != -1) { - mutator.size-- - return mutableCollisionRemoveElementAtIndex(index, mutator.ownership) + if (index == -1) return this + + mutator.size-- + + if (buffer.size == 1) return null + if (ownedBy === mutator.ownership) { + buffer = buffer.removeCellAtIndex(index) + return this } - return this + return makeCollisionNode(buffer.removeCellAtIndex(index), mutator.ownership) + } + + @Suppress("NOTHING_TO_INLINE") + inline fun isCollision(): Boolean { + return bitmap == 0 && buffer.isNotEmpty() } fun contains(elementHash: Int, element: E, shift: Int): Boolean { val cellPositionMask = 1 shl indexSegment(elementHash, shift) if (hasNoCellAt(cellPositionMask)) { // element is absent + if (isCollision()) { + return collisionContains(elementHash, element) + } return false } val cellIndex = indexOfCellAt(cellPositionMask) if (buffer[cellIndex] is TrieNode<*>) { // element may be in node val targetNode = nodeAtIndex(cellIndex) - if (shift == MAX_SHIFT) { - return targetNode.collisionContainsElement(element) - } return targetNode.contains(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) } // element is directly in buffer @@ -259,29 +296,31 @@ internal class TrieNode( val cellPositionMask = 1 shl indexSegment(elementHash, shift) if (hasNoCellAt(cellPositionMask)) { // element is absent + if (isCollision()) { + return collisionAdd(elementHash, element, shift) + } return addElementAt(cellPositionMask, element) } val cellIndex = indexOfCellAt(cellPositionMask) if (buffer[cellIndex] is TrieNode<*>) { // element may be in node val targetNode = nodeAtIndex(cellIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.collisionAdd(element) - } else { - targetNode.add(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) - } + val newNode = targetNode.add(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) if (targetNode === newNode) return this return updateNodeAtIndex(cellIndex, newNode) } // element is directly in buffer if (element == buffer[cellIndex]) return this - return moveElementToNode(cellIndex, elementHash, element, shift) + return moveElementToNode(cellIndex, elementHash, element, shift, null) } fun mutableAdd(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): TrieNode { val cellPosition = 1 shl indexSegment(elementHash, shift) if (hasNoCellAt(cellPosition)) { // element is absent + if (isCollision()) { + return collisionMutableAdd(elementHash, element, shift, mutator) + } mutator.size++ return mutableAddElementAt(cellPosition, element, mutator.ownership) } @@ -289,35 +328,30 @@ internal class TrieNode( val cellIndex = indexOfCellAt(cellPosition) if (buffer[cellIndex] is TrieNode<*>) { // element may be in node val targetNode = nodeAtIndex(cellIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.mutableCollisionAdd(element, mutator) - } else { - targetNode.mutableAdd(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator) - } + val newNode = targetNode.mutableAdd(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator) if (targetNode === newNode) return this return mutableUpdateNodeAtIndex(cellIndex, newNode, mutator.ownership) } // element is directly in buffer if (element == buffer[cellIndex]) return this mutator.size++ - return mutableMoveElementToNode(cellIndex, elementHash, element, shift, mutator.ownership) + return moveElementToNode(cellIndex, elementHash, element, shift, mutator.ownership) } fun remove(elementHash: Int, element: E, shift: Int): TrieNode? { val cellPositionMask = 1 shl indexSegment(elementHash, shift) if (hasNoCellAt(cellPositionMask)) { // element is absent + if (isCollision()) { + return collisionRemove(elementHash, element) + } return this } val cellIndex = indexOfCellAt(cellPositionMask) if (buffer[cellIndex] is TrieNode<*>) { // element may be in node val targetNode = nodeAtIndex(cellIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.collisionRemove(element) - } else { - targetNode.remove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) - } + val newNode = targetNode.remove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) return when { targetNode === newNode -> this newNode == null -> removeCellAtIndex(cellIndex, cellPositionMask) @@ -335,17 +369,16 @@ internal class TrieNode( val cellPositionMask = 1 shl indexSegment(elementHash, shift) if (hasNoCellAt(cellPositionMask)) { // element is absent + if (isCollision()) { + return collisionMutableRemove(elementHash, element, mutator) + } return this } val cellIndex = indexOfCellAt(cellPositionMask) if (buffer[cellIndex] is TrieNode<*>) { // element may be in node val targetNode = nodeAtIndex(cellIndex) - val newNode = if (shift == MAX_SHIFT) { - targetNode.mutableCollisionRemove(element, mutator) - } else { - targetNode.mutableRemove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator) - } + val newNode = targetNode.mutableRemove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator) return when { targetNode === newNode -> this newNode == null -> mutableRemoveCellAtIndex(cellIndex, cellPositionMask, mutator.ownership) diff --git a/core/commonTest/src/stress/map/PersistentHashMapTest.kt b/core/commonTest/src/stress/map/PersistentHashMapTest.kt index a738f2bd..4562d72e 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapTest.kt @@ -109,6 +109,22 @@ class PersistentHashMapTest : ExecutionTimeMeasuringTest() { } } + @Test + fun iteratorTests() { + val emptyIterator = persistentHashMapOf().iterator() + assertFalse(emptyIterator.hasNext()) + assertFails { emptyIterator.next() } + + val collisionIterator = persistentHashMapOf().put(IntWrapper(1, 1), "a").put(IntWrapper(2, 1), "b").iterator() + var aFlag = false + var bFlag = false + for (entry in collisionIterator) { + aFlag = aFlag || entry.value == "a" + bFlag = bFlag || entry.value == "b" + } + assertTrue(aFlag && bFlag) + } + @Test fun removeTests() { var map = persistentHashMapOf() diff --git a/core/commonTest/src/stress/set/PersistentHashSetTest.kt b/core/commonTest/src/stress/set/PersistentHashSetTest.kt index 0f9bc1fa..78c37422 100644 --- a/core/commonTest/src/stress/set/PersistentHashSetTest.kt +++ b/core/commonTest/src/stress/set/PersistentHashSetTest.kt @@ -177,7 +177,9 @@ class PersistentHashSetTest : ExecutionTimeMeasuringTest() { fun collisionTests() { var set = persistentHashSetOf() - assertTrue(set.add(IntWrapper(1, 1)).contains(IntWrapper(1, 1))) + val oneWrapper = IntWrapper(1, 1) + val twoWrapper = IntWrapper(2, 1) + assertTrue(set.add(oneWrapper).add(twoWrapper).run { contains(oneWrapper) && contains(twoWrapper) }) val elementsToAdd = NForAlgorithmComplexity.O_NlogN