[#2] hrw: add typed methods for hashers #3
2 changed files with 165 additions and 0 deletions
34
hrw.go
34
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 {
|
||||
|
|
131
hrw_test.go
131
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue