diff --git a/schulze.go b/schulze.go index 357ae88..a3635f7 100644 --- a/schulze.go +++ b/schulze.go @@ -401,6 +401,9 @@ func calculatePairwiseStrengths[C comparable](choices []C, preferences []int) [] } } + // optimize most inner loop by loop unrolling + const step = 8 + for i := uintptr(0); i < choicesCount; i++ { icc := i * choicesCount @@ -409,19 +412,20 @@ func calculatePairwiseStrengths[C comparable](choices []C, preferences []int) [] ji := jcc + i jip := *(*int)(unsafe.Add(strengthsPtr, ji*intSize)) - for k := uintptr(0); k < choicesCount; k++ { - - ik := icc + k - m := min( - jip, - *(*int)(unsafe.Add(strengthsPtr, ik*intSize)), - ) - - jk := jcc + k - jkp := (*int)(unsafe.Add(strengthsPtr, jk*intSize)) - if m > *jkp { - *jkp = m - } + ccMod := choicesCount % step + cc := choicesCount - ccMod + for k := uintptr(0); k < cc; k += step { + setStrengthValue(strengthsPtr, icc, jcc, k, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+1, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+2, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+3, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+4, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+5, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+6, jip) + setStrengthValue(strengthsPtr, icc, jcc, k+7, jip) + } + for k := uintptr(cc); k < choicesCount; k++ { + setStrengthValue(strengthsPtr, icc, jcc, k, jip) } } } @@ -429,6 +433,20 @@ func calculatePairwiseStrengths[C comparable](choices []C, preferences []int) [] return strengths } +func setStrengthValue(strengthsPtr unsafe.Pointer, icc, jcc, k uintptr, jip int) { + ik := icc + k + m := min( + jip, + *(*int)(unsafe.Add(strengthsPtr, ik*intSize)), + ) + + jk := jcc + k + jkp := (*int)(unsafe.Add(strengthsPtr, jk*intSize)) + if m > *jkp { + *jkp = m + } +} + func calculateResults[C comparable](choices []C, strengths []int) (results []Result[C], tie bool) { choicesCount := len(choices) results = make([]Result[C], 0, choicesCount) diff --git a/schulze_test.go b/schulze_test.go index 4ed2e7a..b03799c 100644 --- a/schulze_test.go +++ b/schulze_test.go @@ -1140,7 +1140,7 @@ func BenchmarkVote(b *testing.B) { func BenchmarkVoting_Results(b *testing.B) { random := rand.New(rand.NewSource(time.Now().UnixNano())) - const choicesCount = 100 + const choicesCount = 1000 choices := newChoices(choicesCount) @@ -1169,7 +1169,7 @@ func BenchmarkVoting_Results(b *testing.B) { func BenchmarkResults(b *testing.B) { random := rand.New(rand.NewSource(time.Now().UnixNano())) - const choicesCount = 100 + const choicesCount = 1000 choices := newChoices(choicesCount) preferences := schulze.NewPreferences(choicesCount)