From 00daf998807fa95688409496b64163dab84fb6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jano=C5=A1=20Gulja=C5=A1?= Date: Tue, 17 Jan 2023 13:02:15 +0100 Subject: [PATCH] Optimize pairwise strengths computation (#2) --- schulze.go | 70 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/schulze.go b/schulze.go index 2721b4e..249daa2 100644 --- a/schulze.go +++ b/schulze.go @@ -9,6 +9,7 @@ package schulze import ( "fmt" "sort" + "unsafe" ) // NewPreferences initializes a fixed size slice that stores all pairwise @@ -166,37 +167,48 @@ func ballotRanks[C comparable](choices []C, b Ballot[C]) (ranks [][]choiceIndex, return ranks, choicesLen, nil } -func calculatePairwiseStrengths[C comparable](choices []C, preferences []int) []strength { - choicesCount := len(choices) +const intSize = unsafe.Sizeof(int(0)) - strengths := make([]strength, choicesCount*choicesCount) +func calculatePairwiseStrengths[C comparable](choices []C, preferences []int) []int { + choicesCount := uintptr(len(choices)) - for i := 0; i < choicesCount; i++ { - for j := 0; j < choicesCount; j++ { - if i != j { - c := preferences[i*choicesCount+j] - if c > preferences[j*choicesCount+i] { - strengths[i*choicesCount+j] = strength(c) - } + if choicesCount == 0 { + return nil + } + + strengths := make([]int, choicesCount*choicesCount) + + strengthsPtr := unsafe.Pointer(&strengths[0]) + + for i := uintptr(0); i < choicesCount; i++ { + for j := uintptr(0); j < choicesCount; j++ { + // removed unnecessary check for optimization: if i == j { continue } + ij := i*choicesCount + j + ji := j*choicesCount + i + c := preferences[ij] + if c > preferences[ji] { + *(*int)(unsafe.Add(strengthsPtr, ij*intSize)) = c } } } - for i := 0; i < choicesCount; i++ { - for j := 0; j < choicesCount; j++ { - if i != j { - for k := 0; k < choicesCount; k++ { - if (i != k) && (j != k) { - jk := j*choicesCount + k - strengths[jk] = max( - strengths[jk], - min( - strengths[j*choicesCount+i], - strengths[i*choicesCount+k], - ), - ) - } - } + for i := uintptr(0); i < choicesCount; i++ { + for j := uintptr(0); j < choicesCount; j++ { + // removed unnecessary check for optimization: if i == j { continue } + for k := uintptr(0); k < choicesCount; k++ { + // removed unnecessary check for optimization: if i == k || j == k { continue } + jk := j*choicesCount + k + ji := j*choicesCount + i + ik := i*choicesCount + k + jkp := (*int)(unsafe.Add(strengthsPtr, jk*intSize)) + m := max( + *jkp, + min( + *(*int)(unsafe.Add(strengthsPtr, ji*intSize)), + *(*int)(unsafe.Add(strengthsPtr, ik*intSize)), + ), + ) + *(jkp) = m } } } @@ -204,7 +216,7 @@ func calculatePairwiseStrengths[C comparable](choices []C, preferences []int) [] return strengths } -func calculateResults[C comparable](choices []C, strengths []strength) (results []Result[C], tie bool) { +func calculateResults[C comparable](choices []C, strengths []int) (results []Result[C], tie bool) { choicesCount := len(choices) results = make([]Result[C], 0, choicesCount) @@ -236,16 +248,14 @@ func calculateResults[C comparable](choices []C, strengths []strength) (results return results, tie } -type strength int - -func min(a, b strength) strength { +func min(a, b int) int { if a < b { return a } return b } -func max(a, b strength) strength { +func max(a, b int) int { if a > b { return a }