From 928193cb688b2876314748603ee0d303f40a3c89 Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Mon, 27 Feb 2023 10:47:17 +0300 Subject: [PATCH 1/3] [#2] hrw: Go version update Go version updated 1.14 -> 1.18 Signed-off-by: Dmitrii Stepanov --- go.mod | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bc3fa8b..8ebc7ee 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,13 @@ module github.com/TrueCloudLab/hrw -go 1.14 +go 1.18 require ( github.com/spaolacci/murmur3 v1.1.0 github.com/stretchr/testify v1.3.0 ) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) -- 2.45.2 From 27ffb96b3f26cc79fe907c33d0ae73e81e5b0427 Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Mon, 27 Feb 2023 10:52:20 +0300 Subject: [PATCH 2/3] [#2] hrw: Add typed methods for hashers Add generic methods for sort-methods used in node Signed-off-by: Dmitrii Stepanov --- hrw.go | 34 ++++++++++++++ hrw_test.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) diff --git a/hrw.go b/hrw.go index 07087df..8ceae9e 100644 --- a/hrw.go +++ b/hrw.go @@ -83,6 +83,17 @@ func SortSliceByValue(slice interface{}, hash uint64) { } } +// SortHasherSliceByValue receives []Hasher and hash to sort by value-distance. +func SortHasherSliceByValue[T Hasher](slice []T, hash uint64) { + rule := prepareHasherRule(slice) + if rule != nil { + swap := func(i, j int) { + slice[i], slice[j] = slice[j], slice[i] + } + sortByDistance(len(rule), false, rule, hash, swap) + } +} + // SortSliceByWeightValue received []T, weights and hash to sort by value-distance * weights func SortSliceByWeightValue(slice interface{}, weights []float64, hash uint64) { rule := prepareRule(slice) @@ -92,6 +103,17 @@ func SortSliceByWeightValue(slice interface{}, weights []float64, hash uint64) { } } +// SortHasherSliceByWeightValue receives []Hasher, weights and hash to sort by value-distance * weights. +func SortHasherSliceByWeightValue[T Hasher](slice []T, weights []float64, hash uint64) { + rule := prepareHasherRule(slice) + if rule != nil { + swap := func(i, j int) { + slice[i], slice[j] = slice[j], slice[i] + } + sortByWeight(len(slice), false, rule, weights, hash, swap) + } +} + // SortSliceByIndex received []T and hash to sort by index-distance func SortSliceByIndex(slice interface{}, hash uint64) { length := reflect.ValueOf(slice).Len() @@ -199,6 +221,18 @@ func prepareRule(slice interface{}) []uint64 { return rule } +func prepareHasherRule[T Hasher](hashers []T) []uint64 { + length := len(hashers) + if length == 0 { + return nil + } + result := make([]uint64, length) + for i := 0; i < length; i++ { + result[i] = hashers[i].Hash() + } + return result +} + // ValidateWeights checks if weights are normalized between 0.0 and 1.0 func ValidateWeights(weights []float64) error { for i := range weights { diff --git a/hrw_test.go b/hrw_test.go index 5173572..7eb3d7e 100644 --- a/hrw_test.go +++ b/hrw_test.go @@ -131,6 +131,23 @@ func TestSortSliceByValueHasher(t *testing.T) { require.Equal(t, expect, actual) } +func TestSortHasherSliceByValue(t *testing.T) { + actual := []hashString{"a", "b", "c", "d", "e", "f"} + expect := []hashString{"d", "f", "c", "b", "a", "e"} + hash := Hash(testKey) + SortHasherSliceByValue(actual, hash) + require.EqualValues(t, expect, actual) +} + +func TestSortHasherSliceByWeightValue(t *testing.T) { + actual := []hashString{"a", "b", "c", "d", "e", "f"} + weights := []float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0} + expect := []hashString{"d", "f", "c", "b", "a", "e"} + hash := Hash(testKey) + SortHasherSliceByWeightValue(actual, weights, hash) + require.EqualValues(t, expect, actual) +} + func TestSortSliceByValueIntSlice(t *testing.T) { cases := []slices{ { @@ -636,6 +653,36 @@ func BenchmarkSortByValue_fnv_1000(b *testing.B) { benchmarkSortByValue(b, 1000, hash) } +func BenchmarkSortHashersByValue_Reflection_fnv_10(b *testing.B) { + hash := Hash(testKey) + benchmarkSortHashersByValueReflection(b, 10, hash) +} + +func BenchmarkSortHashersByValue_Reflection_fnv_100(b *testing.B) { + hash := Hash(testKey) + benchmarkSortHashersByValueReflection(b, 100, hash) +} + +func BenchmarkSortHashersByValue_Reflection_fnv_1000(b *testing.B) { + hash := Hash(testKey) + benchmarkSortHashersByValueReflection(b, 1000, hash) +} + +func BenchmarkSortHashersByValue_Typed_fnv_10(b *testing.B) { + hash := Hash(testKey) + benchmarkSortHashersByValueTyped(b, 10, hash) +} + +func BenchmarkSortHashersByValue_Typed_fnv_100(b *testing.B) { + hash := Hash(testKey) + benchmarkSortHashersByValueTyped(b, 100, hash) +} + +func BenchmarkSortHashersByValue_Typed_fnv_1000(b *testing.B) { + hash := Hash(testKey) + benchmarkSortHashersByValueTyped(b, 1000, hash) +} + func BenchmarkSortByWeight_fnv_10(b *testing.B) { hash := Hash(testKey) _ = benchmarkSortByWeight(b, 10, hash) @@ -681,6 +728,30 @@ func BenchmarkSortByWeightValue_fnv_1000(b *testing.B) { benchmarkSortByWeightValue(b, 1000, hash) } +func BenchmarkSortHashersByWeightValueReflection_fnv_10(b *testing.B) { + benchmarkSortHashersByWeightValueRelection(b, 10, Hash(testKey)) +} + +func BenchmarkSortHashersByWeightValueReflection_fnv_100(b *testing.B) { + benchmarkSortHashersByWeightValueRelection(b, 100, Hash(testKey)) +} + +func BenchmarkSortHashersByWeightValueReflection_fnv_1000(b *testing.B) { + benchmarkSortHashersByWeightValueRelection(b, 100, Hash(testKey)) +} + +func BenchmarkSortHashersByWeightValueTyped_fnv_10(b *testing.B) { + benchmarkSortHashersByWeightValueTyped(b, 10, Hash(testKey)) +} + +func BenchmarkSortHashersByWeightValueTyped_fnv_100(b *testing.B) { + benchmarkSortHashersByWeightValueTyped(b, 100, Hash(testKey)) +} + +func BenchmarkSortHashersByWeightValueTyped_fnv_1000(b *testing.B) { + benchmarkSortHashersByWeightValueTyped(b, 100, Hash(testKey)) +} + func benchmarkSort(b *testing.B, n int, hash uint64) uint64 { servers := make([]uint64, n) for i := uint64(0); i < uint64(len(servers)); i++ { @@ -774,3 +845,63 @@ func benchmarkSortByWeightValue(b *testing.B, n int, hash uint64) { SortSliceByWeightValue(servers, weights, hash) } } + +func benchmarkSortHashersByWeightValueRelection(b *testing.B, n int, hash uint64) { + servers := make([]hashString, n) + weights := make([]float64, n) + for i := uint64(0); i < uint64(len(servers)); i++ { + weights[i] = float64(uint64(n)-i) / float64(n) + servers[i] = hashString("localhost:" + strconv.FormatUint(60000-i, 10)) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + SortSliceByWeightValue(servers, weights, hash) + } +} + +func benchmarkSortHashersByWeightValueTyped(b *testing.B, n int, hash uint64) { + servers := make([]hashString, n) + weights := make([]float64, n) + for i := uint64(0); i < uint64(len(servers)); i++ { + weights[i] = float64(uint64(n)-i) / float64(n) + servers[i] = hashString("localhost:" + strconv.FormatUint(60000-i, 10)) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + SortHasherSliceByWeightValue(servers, weights, hash) + } +} + +func benchmarkSortHashersByValueReflection(b *testing.B, n int, hash uint64) { + servers := make([]hashString, n) + for i := uint64(0); i < uint64(len(servers)); i++ { + servers[i] = hashString("localhost:" + strconv.FormatUint(60000-i, 10)) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + SortSliceByValue(servers, hash) + } +} + +func benchmarkSortHashersByValueTyped(b *testing.B, n int, hash uint64) { + servers := make([]hashString, n) + for i := uint64(0); i < uint64(len(servers)); i++ { + servers[i] = hashString("localhost:" + strconv.FormatUint(60000-i, 10)) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + SortHasherSliceByValue(servers, hash) + } +} -- 2.45.2 From b4b224a186055f90f66fbd5f5b5950492e16c798 Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Mon, 27 Feb 2023 12:11:27 +0300 Subject: [PATCH 3/3] [#3] hrw: Update README Update benchmark results Signed-off-by: Dmitrii Stepanov --- README.md | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 29aa338..3a5182c 100644 --- a/README.md +++ b/README.md @@ -14,25 +14,37 @@ ## Benchmark: ``` -BenchmarkSort_fnv_10-8 5000000 365 ns/op 224 B/op 3 allocs/op -BenchmarkSort_fnv_100-8 300000 5261 ns/op 1856 B/op 3 allocs/op -BenchmarkSort_fnv_1000-8 10000 119462 ns/op 16448 B/op 3 allocs/op -BenchmarkSortByIndex_fnv_10-8 3000000 546 ns/op 384 B/op 7 allocs/op -BenchmarkSortByIndex_fnv_100-8 200000 5965 ns/op 2928 B/op 7 allocs/op -BenchmarkSortByIndex_fnv_1000-8 10000 127732 ns/op 25728 B/op 7 allocs/op -BenchmarkSortByValue_fnv_10-8 2000000 962 ns/op 544 B/op 17 allocs/op -BenchmarkSortByValue_fnv_100-8 200000 9604 ns/op 4528 B/op 107 allocs/op -BenchmarkSortByValue_fnv_1000-8 10000 111741 ns/op 41728 B/op 1007 allocs/op +BenchmarkSort_fnv_10-8 4812801 240.9 ns/op 216 B/op 4 allocs/op +BenchmarkSort_fnv_100-8 434767 2600 ns/op 1848 B/op 4 allocs/op +BenchmarkSort_fnv_1000-8 20428 66116 ns/op 16440 B/op 4 allocs/op +BenchmarkSortByIndex_fnv_10-8 2505410 486.5 ns/op 352 B/op 7 allocs/op +BenchmarkSortByIndex_fnv_100-8 254556 4697 ns/op 1984 B/op 7 allocs/op +BenchmarkSortByIndex_fnv_1000-8 13581 88334 ns/op 16576 B/op 7 allocs/op +BenchmarkSortByValue_fnv_10-8 1761030 682.1 ns/op 592 B/op 18 allocs/op +BenchmarkSortByValue_fnv_100-8 258838 4675 ns/op 4480 B/op 108 allocs/op +BenchmarkSortByValue_fnv_1000-8 27027 44649 ns/op 40768 B/op 1008 allocs/op +BenchmarkSortHashersByValue_Reflection_fnv_10-8 1013560 1249 ns/op 768 B/op 29 allocs/op +BenchmarkSortHashersByValue_Reflection_fnv_100-8 106029 11414 ns/op 6096 B/op 209 allocs/op +BenchmarkSortHashersByValue_Reflection_fnv_1000-8 10000 108977 ns/op 56784 B/op 2009 allocs/op +BenchmarkSortHashersByValue_Typed_fnv_10-8 1577814 700.3 ns/op 584 B/op 17 allocs/op +BenchmarkSortHashersByValue_Typed_fnv_100-8 215938 5024 ns/op 4472 B/op 107 allocs/op +BenchmarkSortHashersByValue_Typed_fnv_1000-8 24447 46889 ns/op 40760 B/op 1007 allocs/op -BenchmarkSortByWeight_fnv_10-8 3000000 501 ns/op 320 B/op 4 allocs/op -BenchmarkSortByWeight_fnv_100-8 200000 8495 ns/op 2768 B/op 4 allocs/op -BenchmarkSortByWeight_fnv_1000-8 10000 197880 ns/op 24656 B/op 4 allocs/op -BenchmarkSortByWeightIndex_fnv_10-8 2000000 702 ns/op 480 B/op 8 allocs/op -BenchmarkSortByWeightIndex_fnv_100-8 200000 9338 ns/op 3840 B/op 8 allocs/op -BenchmarkSortByWeightIndex_fnv_1000-8 10000 204669 ns/op 33936 B/op 8 allocs/op -BenchmarkSortByWeightValue_fnv_10-8 1000000 1083 ns/op 640 B/op 18 allocs/op -BenchmarkSortByWeightValue_fnv_100-8 200000 11444 ns/op 5440 B/op 108 allocs/op -BenchmarkSortByWeightValue_fnv_1000-8 10000 148471 ns/op 49936 B/op 1008 allocs/op +BenchmarkSortByWeight_fnv_10-8 2924833 370.6 ns/op 448 B/op 8 allocs/op +BenchmarkSortByWeight_fnv_100-8 816069 1516 ns/op 2896 B/op 8 allocs/op +BenchmarkSortByWeight_fnv_1000-8 80391 17478 ns/op 24784 B/op 8 allocs/op +BenchmarkSortByWeightIndex_fnv_10-8 1945612 550.3 ns/op 368 B/op 7 allocs/op +BenchmarkSortByWeightIndex_fnv_100-8 140473 8084 ns/op 2000 B/op 7 allocs/op +BenchmarkSortByWeightIndex_fnv_1000-8 5518 200949 ns/op 16592 B/op 7 allocs/op +BenchmarkSortByWeightValue_fnv_10-8 1305580 909.8 ns/op 608 B/op 18 allocs/op +BenchmarkSortByWeightValue_fnv_100-8 165410 6796 ns/op 4496 B/op 108 allocs/op +BenchmarkSortByWeightValue_fnv_1000-8 17922 78555 ns/op 40784 B/op 1008 allocs/op +BenchmarkSortHashersByWeightValueReflection_fnv_10-8 454976 2229 ns/op 784 B/op 29 allocs/op +BenchmarkSortHashersByWeightValueReflection_fnv_100-8 76264 15332 ns/op 6112 B/op 209 allocs/op +BenchmarkSortHashersByWeightValueReflection_fnv_1000-8 80288 13192 ns/op 6112 B/op 209 allocs/op +BenchmarkSortHashersByWeightValueTyped_fnv_10-8 1433113 901.4 ns/op 600 B/op 17 allocs/op +BenchmarkSortHashersByWeightValueTyped_fnv_100-8 188626 5896 ns/op 4488 B/op 107 allocs/op +BenchmarkSortHashersByWeightValueTyped_fnv_1000-8 178131 6518 ns/op 4488 B/op 107 allocs/op ``` ## Example -- 2.45.2