-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #452 from JohnLCaron/encryptTiming
Add cli to run encryption timing.
- Loading branch information
Showing
6 changed files
with
313 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
egklib/src/commonTest/kotlin/electionguard/core/PowRadixTiming.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package electionguard.core | ||
|
||
import electionguard.util.Stopwatch | ||
import electionguard.util.pad | ||
import electionguard.util.sigfig | ||
import kotlin.test.Test | ||
|
||
class PowRadixTiming { | ||
|
||
@Test | ||
fun timeLow() { | ||
println("PowRadixOption.LOW_MEMORY_USE") | ||
val groupLow = productionGroup(PowRadixOption.LOW_MEMORY_USE, ProductionMode.Mode4096 ) | ||
val nonce = groupLow.randomElementModQ() | ||
groupLow.gPowP(nonce) // first time fills table | ||
testWarmup(groupLow) | ||
} | ||
// PowRadixOption.LOW_MEMORY_USE | ||
// 0 acc 1000 = .851 msec per acc | ||
//1000 acc 1000 = .848 msec per acc | ||
//2000 acc 1000 = .852 msec per acc | ||
//3000 acc 1000 = .759 msec per acc // HotSpot warmup | ||
//4000 acc 1000 = .757 msec per acc | ||
//5000 acc 1000 = .752 msec per acc | ||
//6000 acc 1000 = .749 msec per acc | ||
//7000 acc 1000 = .755 msec per acc | ||
//8000 acc 1000 = .755 msec per acc | ||
//9000 acc 1000 = .752 msec per acc | ||
|
||
@Test | ||
fun timeHigh() { | ||
println("PowRadixOption.HIGH_MEMORY_USE") | ||
val groupHigh = productionGroup(PowRadixOption.HIGH_MEMORY_USE, ProductionMode.Mode4096 ) | ||
val nonce = groupHigh.randomElementModQ() | ||
groupHigh.gPowP(nonce) // first time fills table | ||
testWarmup(groupHigh) | ||
} | ||
// PowRadixOption.HIGH_MEMORY_USE | ||
// 0 acc 1000 = .670 msec per acc | ||
//1000 acc 1000 = .561 msec per acc | ||
//2000 acc 1000 = .615 msec per acc | ||
//3000 acc 1000 = .588 msec per acc | ||
//4000 acc 1000 = .559 msec per acc | ||
//5000 acc 1000 = .573 msec per acc | ||
//6000 acc 1000 = .553 msec per acc | ||
//7000 acc 1000 = .554 msec per acc | ||
//8000 acc 1000 = .536 msec per acc | ||
//9000 acc 1000 = .540 msec per acc | ||
|
||
@Test | ||
fun timeExtreme() { | ||
println("PowRadixOption.EXTREME_MEMORY_USE") | ||
val groupExtreme = productionGroup(PowRadixOption.EXTREME_MEMORY_USE, ProductionMode.Mode4096 ) | ||
val nonce = groupExtreme.randomElementModQ() | ||
groupExtreme.gPowP(nonce) // first time fills table | ||
testWarmup(groupExtreme) | ||
} | ||
// PowRadixOption.EXTREME_MEMORY_USE | ||
// 0 acc 1000 = .463 msec per acc | ||
//1000 acc 1000 = .421 msec per acc | ||
//2000 acc 1000 = .415 msec per acc | ||
//3000 acc 1000 = .427 msec per acc | ||
//4000 acc 1000 = .422 msec per acc | ||
//5000 acc 1000 = .492 msec per acc | ||
//6000 acc 1000 = .406 msec per acc | ||
//7000 acc 1000 = .404 msec per acc | ||
//8000 acc 1000 = .402 msec per acc | ||
//9000 acc 1000 = .480 msec per acc | ||
|
||
fun testWarmup(group: GroupContext) { | ||
var count = 0 | ||
val incr = 1000 | ||
repeat(10) { | ||
timeAcc(group, count, incr) | ||
count += incr | ||
} | ||
} | ||
|
||
fun timeAcc(group: GroupContext, count: Int, n:Int) { | ||
val nonces = List(n) { group.randomElementModQ() } | ||
|
||
var stopwatch = Stopwatch() | ||
repeat(n) { require( !group.gPowP(nonces[it]).isZero()) } | ||
var duration = stopwatch.stop() | ||
val peracc = duration.toDouble() / n / 1_000_000 | ||
println(" ${count.pad(4)} acc $n = ${peracc.sigfig(3)} msec per acc") | ||
} | ||
|
||
@Test | ||
// compare exp vs acc | ||
fun timeExp() { | ||
println("compare exp vs acc") | ||
val group = productionGroup() | ||
compareExp(group,100) | ||
compareExp(group,1000) | ||
compareExp(group,10000) | ||
compareExp(group,20000) | ||
} | ||
|
||
fun compareExp(group: GroupContext, n:Int) { | ||
val nonces = List(n) { group.randomElementModQ() } | ||
val h = group.gPowP(group.randomElementModQ()) | ||
|
||
var stopwatch = Stopwatch() | ||
repeat(n) { require( !group.gPowP(nonces[it]).isZero()) } | ||
|
||
var duration = stopwatch.stop() | ||
val peracc = duration.toDouble() / n / 1_000_000 | ||
println(" acc took $duration msec for $n = $peracc msec per acc") | ||
|
||
stopwatch.start() | ||
repeat(n) { require(!(h powP nonces[it]).isZero()) } | ||
|
||
duration = stopwatch.stop() | ||
val perexp = duration.toDouble() / n / 1_000_000 | ||
println(" exp took $duration msec for $n = $perexp msec per exp") | ||
|
||
println(" exp/acc took ${perexp/peracc}") | ||
} | ||
|
||
|
||
@Test | ||
fun timeMultiply() { | ||
println("compare exp vs acc") | ||
val group = productionGroup() | ||
timeMultiply(group,1000) | ||
timeMultiply(group,10000) | ||
timeMultiply(group,20000) | ||
} | ||
|
||
fun timeMultiply(group: GroupContext, n:Int) { | ||
val nonces = List(n) { group.randomElementModQ() } | ||
val elemps = nonces.map { group.gPowP(it) } | ||
|
||
var starting = getSystemTimeInMillis() | ||
val prod = elemps.reduce { a, b -> a * b } | ||
var duration = getSystemTimeInMillis() - starting | ||
var peracc = duration.toDouble() / n | ||
println(" multiply took $duration msec for $n = $peracc msec per multiply") | ||
|
||
starting = getSystemTimeInMillis() | ||
elemps.forEach { it * it } | ||
duration = getSystemTimeInMillis() - starting | ||
peracc = duration.toDouble() / n | ||
println(" square took $duration msec for $n = $peracc msec per multiply") | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
egklib/src/commonTest/kotlin/electionguard/encrypt/RunEncryptBallotTimingTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package electionguard.encrypt | ||
|
||
import electionguard.cli.RunEncryptBallotTiming | ||
import kotlin.test.Test | ||
|
||
class | ||
RunEncryptBallotTimingTest { | ||
|
||
@Test | ||
fun testRunEncryptBallotTiming() { | ||
RunEncryptBallotTiming.main( | ||
arrayOf( | ||
) | ||
) | ||
} | ||
|
||
} |
94 changes: 94 additions & 0 deletions
94
egklib/src/jvmMain/kotlin/electionguard/cli/RunEncryptBallotTiming.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package electionguard.cli | ||
|
||
import electionguard.core.* | ||
import electionguard.encrypt.Encryptor | ||
import electionguard.input.RandomBallotProvider | ||
import electionguard.util.ErrorMessages | ||
import electionguard.util.Stopwatch | ||
import electionguard.util.sigfig | ||
import kotlinx.cli.ArgParser | ||
import kotlinx.cli.ArgType | ||
import kotlinx.cli.default | ||
|
||
class RunEncryptBallotTiming { | ||
|
||
companion object { | ||
@JvmStatic | ||
fun main(args: Array<String>) { | ||
val parser = ArgParser("RunEncryptBallotTiming") | ||
val nballots by parser.option( | ||
ArgType.Int, | ||
shortName = "nballots", | ||
description = "Number of ballots to encrypt and measure time" | ||
).default(100) | ||
val ncontests by parser.option( | ||
ArgType.Int, | ||
shortName = "ncontests", | ||
description = "Number of contests per ballot" | ||
).default(12) | ||
val nselections by parser.option( | ||
ArgType.Int, | ||
shortName = "nselections", | ||
description = "Number of selections per contest" | ||
).default(4) | ||
val warmup by parser.option( | ||
ArgType.Int, | ||
shortName = "warmup", | ||
description = "Number of ballots to encrypt as warmup (not timed)" | ||
).default(11) | ||
val showOperations by parser.option( | ||
ArgType.Boolean, | ||
shortName = "ops", | ||
description = "Show operation count" | ||
).default(false) | ||
parser.parse(args) | ||
|
||
println( | ||
"RunEncryptBallotTiming\n" + | ||
" nballots = '$nballots'\n" + | ||
" ncontests = '$ncontests'\n" + | ||
" nselections = '$nselections'\n" + | ||
" warmup = '$warmup'\n" | ||
) | ||
|
||
val group = productionGroup() | ||
val manifest = buildTestManifest(ncontests, nselections) | ||
val keypair = elGamalKeyPairFromRandom(group) | ||
val encryptor = Encryptor(group, manifest, keypair.publicKey, UInt256.random(), "device") | ||
|
||
// warmup | ||
println("warming up with $warmup ballots") | ||
val warmupProvider = RandomBallotProvider(manifest, warmup) | ||
warmupProvider.ballots().forEach { ballot -> | ||
val encryptedBallot = encryptor.encrypt(ballot, ByteArray(0), ErrorMessages("testEncryption")) | ||
requireNotNull(encryptedBallot) | ||
} | ||
|
||
// time it | ||
val ballotProvider = RandomBallotProvider(manifest, nballots) | ||
var stopwatch = Stopwatch() | ||
group.getAndClearOpCounts() | ||
ballotProvider.ballots().forEach { ballot -> | ||
val encryptedBallot = encryptor.encrypt(ballot, ByteArray(0), ErrorMessages("testEncryption")) | ||
requireNotNull(encryptedBallot) | ||
} | ||
val opCounts = group.getAndClearOpCounts() | ||
var duration = stopwatch.stop() | ||
|
||
val perballot = duration.toDouble() / nballots / 1_000_000 | ||
val nencryptions = ncontests + ncontests * nselections | ||
val perencryption = perballot / nencryptions | ||
println("Encryption took ${duration / 1_000_000_000 } secs for $nballots ballots") | ||
println(" ${perballot.sigfig(3)} msec per ballot") | ||
println(" ${perencryption.sigfig(3)} msec per encryption ($nencryptions encryptions/ballot)") | ||
println() | ||
if (showOperations) { | ||
println("operations:") | ||
println(buildString { | ||
opCounts.forEach { key, value -> println(" $key = $value") } | ||
}) | ||
println("expect: ${6 * nencryptions * nballots + 2 * nballots}") | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters