perf: pass PointScratch through mul and mulG, eliminate ThreadLocal
Add optional PointScratch parameter to ECPoint.mul and ECPoint.mulG (default to scratch.get() for backward compat). All callers in Secp256k1.kt now pass their already-fetched scratch through. From the ECDH trace: ECPoint.mul was calling scratch.get() (ThreadLocal) redundantly — the caller already had the scratch. On ART, each ThreadLocal.get costs ~6µs (hash table probe), and ECDH had 10 calls totaling 64µs. With this change, all hot-path EC operations (verify, sign, ECDH, pubkey create, tweak mul) fetch the scratch once at the entry point and pass it through the entire call chain. https://claude.ai/code/session_015CtM5k88rF7WFgX8o2AGNR
This commit is contained in:
@@ -455,13 +455,12 @@ internal object ECPoint {
|
|||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
p: MutablePoint,
|
p: MutablePoint,
|
||||||
scalar: LongArray,
|
scalar: LongArray,
|
||||||
|
s: PointScratch = scratch.get(),
|
||||||
) {
|
) {
|
||||||
if (U256.isZero(scalar) || p.isInfinity()) {
|
if (U256.isZero(scalar) || p.isInfinity()) {
|
||||||
out.setInfinity()
|
out.setInfinity()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val s = scratch.get()
|
|
||||||
val wnd = 5
|
val wnd = 5
|
||||||
val tableSize = 1 shl (wnd - 2) // 8 entries
|
val tableSize = 1 shl (wnd - 2) // 8 entries
|
||||||
|
|
||||||
@@ -538,13 +537,12 @@ internal object ECPoint {
|
|||||||
fun mulG(
|
fun mulG(
|
||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
scalar: LongArray,
|
scalar: LongArray,
|
||||||
|
s: PointScratch = scratch.get(),
|
||||||
) {
|
) {
|
||||||
if (U256.isZero(scalar)) {
|
if (U256.isZero(scalar)) {
|
||||||
out.setInfinity()
|
out.setInfinity()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val s = scratch.get()
|
|
||||||
val table = combTable
|
val table = combTable
|
||||||
|
|
||||||
// Ping-pong: alternate between out and s.mixTmp to avoid copyFrom after
|
// Ping-pong: alternate between out and s.mixTmp to avoid copyFrom after
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ object Secp256k1 {
|
|||||||
val scalar = sc.scalarTmp1
|
val scalar = sc.scalarTmp1
|
||||||
U256.fromBytesInto(scalar, seckey, 0)
|
U256.fromBytesInto(scalar, seckey, 0)
|
||||||
require(ScalarN.isValid(scalar))
|
require(ScalarN.isValid(scalar))
|
||||||
ECPoint.mulG(sc.entryResult, scalar)
|
ECPoint.mulG(sc.entryResult, scalar, sc)
|
||||||
check(ECPoint.toAffine(sc.entryResult, sc.entryPx, sc.entryPy, sc))
|
check(ECPoint.toAffine(sc.entryResult, sc.entryPx, sc.entryPy, sc))
|
||||||
return KeyCodec.serializeUncompressed(sc.entryPx, sc.entryPy)
|
return KeyCodec.serializeUncompressed(sc.entryPx, sc.entryPy)
|
||||||
}
|
}
|
||||||
@@ -239,7 +239,7 @@ object Secp256k1 {
|
|||||||
val d0 = U256.fromBytes(seckey)
|
val d0 = U256.fromBytes(seckey)
|
||||||
require(ScalarN.isValid(d0))
|
require(ScalarN.isValid(d0))
|
||||||
val sc = ECPoint.getScratch()
|
val sc = ECPoint.getScratch()
|
||||||
ECPoint.mulG(sc.entryResult, d0)
|
ECPoint.mulG(sc.entryResult, d0, sc)
|
||||||
check(ECPoint.toAffine(sc.entryResult, sc.entryPx, sc.entryPy, sc))
|
check(ECPoint.toAffine(sc.entryResult, sc.entryPx, sc.entryPy, sc))
|
||||||
|
|
||||||
// Allocate xOnlyPub — signSchnorrInternal reuses bytesTmp1/2 internally.
|
// Allocate xOnlyPub — signSchnorrInternal reuses bytesTmp1/2 internally.
|
||||||
@@ -361,7 +361,7 @@ object Secp256k1 {
|
|||||||
require(!U256.isZero(k0))
|
require(!U256.isZero(k0))
|
||||||
|
|
||||||
// R = k0·G
|
// R = k0·G
|
||||||
ECPoint.mulG(sc.entryResult, k0)
|
ECPoint.mulG(sc.entryResult, k0, sc)
|
||||||
val rx = sc.entryPx
|
val rx = sc.entryPx
|
||||||
val ry = sc.entryPy
|
val ry = sc.entryPy
|
||||||
check(ECPoint.toAffine(sc.entryResult, rx, ry, sc))
|
check(ECPoint.toAffine(sc.entryResult, rx, ry, sc))
|
||||||
@@ -552,7 +552,7 @@ object Secp256k1 {
|
|||||||
require(ScalarN.isValid(scalar))
|
require(ScalarN.isValid(scalar))
|
||||||
|
|
||||||
sc.entryPoint.setAffine(sc.entryPx, sc.entryPy)
|
sc.entryPoint.setAffine(sc.entryPx, sc.entryPy)
|
||||||
ECPoint.mul(sc.entryResult, sc.entryPoint, scalar)
|
ECPoint.mul(sc.entryResult, sc.entryPoint, scalar, sc)
|
||||||
check(ECPoint.toAffine(sc.entryResult, sc.entryPx, sc.entryPy, sc))
|
check(ECPoint.toAffine(sc.entryResult, sc.entryPx, sc.entryPy, sc))
|
||||||
|
|
||||||
return if (pubkey.size == 33) {
|
return if (pubkey.size == 33) {
|
||||||
@@ -594,7 +594,7 @@ object Secp256k1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sc.entryPoint.setAffine(sc.entryPx, sc.entryPy)
|
sc.entryPoint.setAffine(sc.entryPx, sc.entryPy)
|
||||||
ECPoint.mul(sc.entryResult, sc.entryPoint, k)
|
ECPoint.mul(sc.entryResult, sc.entryPoint, k, sc)
|
||||||
check(ECPoint.toAffineX(sc.entryResult, sc.entryPx, sc))
|
check(ECPoint.toAffineX(sc.entryResult, sc.entryPx, sc))
|
||||||
return U256.toBytes(sc.entryPx)
|
return U256.toBytes(sc.entryPx)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user