mirror of
https://github.com/bitcoin-core/secp256k1.git
synced 2026-07-21 14:43:37 +00:00
Merge bitcoin-core/secp256k1#1889: field: serialize elements by word
e217ead5c4field: serialize elements by word (Lőrinc) Pull request description: **Problem:** Both field implementations serialize normalized elements with 32 separate byte assignments in `secp256k1_fe_impl_get_b32`. They were introduced in [#437](https://github.com/bitcoin-core/secp256k1/pull/437/changes#diff-6e0cae0111d7c054ba27f22399eb4a2ac6c9788ee97da7f9fc5948c63dcf882cL353) to unroll nested nibble loops for performance. At the time, the endian write helpers did not exist, so the unrolling was expressed directly. **Fix:** Keep both serializers unrolled by packing the 5x52 limbs into four 64-bit words and the 10x26 limbs into eight 32-bit words, then reuse the corresponding endian write helper. This matches the existing [4x64](https://github.com/bitcoin-core/secp256k1/blob/ebf594320dc838b9de1abb54d5ba98cef84f4297/src/scalar_4x64_impl.h#L161-L168) and [8x32](https://github.com/bitcoin-core/secp256k1/blob/ebf594320dc838b9de1abb54d5ba98cef84f4297/src/scalar_8x32_impl.h#L195-L206) scalar serializers. The serialized output is unchanged for normalized inputs, and benchmarks indicate no measurable performance regression. **Testing:** Existing [field conversion and reduced-boundary tests](https://github.com/bitcoin-core/secp256k1/blob/ebf594320dc838b9de1abb54d5ba98cef84f4297/src/tests.c#L3079-L3173) compare the serialized bytes directly. [Compiler Explorer](https://godbolt.org/z/nxaKecdxh) compares the exact before and after serializers with GCC 16.1 and Clang 22.1 at `-O2`, in both native and `-m32` modes. <details><summary>Benchmarks</summary> ```bash for cc in gcc clang; do \ echo; $cc --version | head -1; \ CC=$cc cmake -B build-$cc -GNinja -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1 && \ CC=$cc cmake -B build-$cc-10x26 -GNinja -DCMAKE_BUILD_TYPE=Release -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int64 >/dev/null 2>&1 && \ for build in build-$cc build-$cc-10x26; do \ echo; echo $build; \ for rev inebf594320de217ead5c46062ecda964ba858f7518b7e3bb5e7; do \ git fetch origin -q $rev && git checkout -q $rev && \ ninja -C $build bench >/dev/null 2>&1 && \ echo $rev && \ SECP256K1_BENCH_ITERS=10000 $build/bin/bench; \ done; \ done; \ done ``` ```bash gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0 build-gccebf594320dBenchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 33.9 , 34.0 , 34.0 ecdsa_sign , 21.6 , 21.6 , 21.6 ec_keygen , 15.2 , 15.2 , 15.3 ecdh , 33.4 , 33.5 , 33.5 schnorrsig_sign , 16.1 , 16.1 , 16.2 schnorrsig_verify , 34.3 , 34.3 , 34.4 ellswift_encode , 16.2 , 16.2 , 16.2 ellswift_decode , 7.21 , 7.22 , 7.24 ellswift_keygen , 31.5 , 31.5 , 31.5 ellswift_ecdh , 36.0 , 36.1 , 36.1e217ead5c4Benchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 34.0 , 34.1 , 34.4 ecdsa_sign , 21.6 , 21.6 , 21.6 ec_keygen , 15.3 , 15.3 , 15.3 ecdh , 33.3 , 33.4 , 33.6 schnorrsig_sign , 16.1 , 16.1 , 16.2 schnorrsig_verify , 34.4 , 34.4 , 34.5 ellswift_encode , 16.3 , 16.3 , 16.3 ellswift_decode , 7.24 , 7.24 , 7.25 ellswift_keygen , 31.5 , 31.6 , 31.7 ellswift_ecdh , 36.0 , 36.1 , 36.1 build-gcc-10x26ebf594320dBenchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 58.4 , 58.5 , 58.5 ecdsa_sign , 29.7 , 29.8 , 29.8 ec_keygen , 22.3 , 22.4 , 22.5 ecdh , 56.0 , 56.1 , 56.2 schnorrsig_sign , 23.4 , 23.4 , 23.5 schnorrsig_verify , 58.9 , 59.0 , 59.1 ellswift_encode , 22.7 , 22.7 , 22.7 ellswift_decode , 10.8 , 10.8 , 10.8 ellswift_keygen , 45.1 , 45.2 , 45.3 ellswift_ecdh , 59.6 , 59.7 , 59.8e217ead5c4Benchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 58.5 , 58.6 , 58.7 ecdsa_sign , 29.8 , 29.8 , 29.9 ec_keygen , 22.3 , 22.4 , 22.4 ecdh , 56.0 , 56.1 , 56.4 schnorrsig_sign , 23.5 , 23.5 , 23.5 schnorrsig_verify , 58.9 , 59.0 , 59.1 ellswift_encode , 22.9 , 22.9 , 22.9 ellswift_decode , 10.8 , 10.8 , 10.8 ellswift_keygen , 45.2 , 45.3 , 45.4 ellswift_ecdh , 59.6 , 59.8 , 59.9 Ubuntu clang version 21.1.8 (6ubuntu1) build-clangebf594320dBenchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 36.2 , 36.3 , 36.7 ecdsa_sign , 23.1 , 23.1 , 23.2 ec_keygen , 16.6 , 16.7 , 16.7 ecdh , 35.6 , 35.7 , 35.7 schnorrsig_sign , 17.6 , 17.6 , 17.6 schnorrsig_verify , 36.6 , 36.7 , 36.9 ellswift_encode , 16.6 , 16.6 , 16.7 ellswift_decode , 7.28 , 7.29 , 7.30 ellswift_keygen , 33.2 , 33.2 , 33.3 ellswift_ecdh , 38.5 , 38.5 , 38.6e217ead5c4Benchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 35.5 , 35.6 , 35.8 ecdsa_sign , 23.0 , 23.1 , 23.2 ec_keygen , 16.6 , 16.7 , 16.8 ecdh , 35.7 , 35.7 , 35.8 schnorrsig_sign , 17.6 , 17.6 , 17.7 schnorrsig_verify , 35.9 , 35.9 , 36.0 ellswift_encode , 16.4 , 16.4 , 16.4 ellswift_decode , 7.22 , 7.22 , 7.23 ellswift_keygen , 33.0 , 33.0 , 33.1 ellswift_ecdh , 38.5 , 38.6 , 38.8 build-clang-10x26ebf594320dBenchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 64.2 , 64.3 , 64.7 ecdsa_sign , 33.8 , 33.9 , 33.9 ec_keygen , 26.3 , 26.3 , 26.3 ecdh , 63.4 , 63.6 , 63.8 schnorrsig_sign , 27.2 , 27.3 , 27.3 schnorrsig_verify , 64.5 , 64.6 , 64.7 ellswift_encode , 22.1 , 22.1 , 22.1 ellswift_decode , 10.6 , 10.6 , 10.7 ellswift_keygen , 48.3 , 48.3 , 48.5 ellswift_ecdh , 67.0 , 67.0 , 67.1e217ead5c4Benchmark , Min(us) , Avg(us) , Max(us) ecdsa_verify , 63.9 , 64.0 , 64.3 ecdsa_sign , 33.8 , 33.9 , 34.0 ec_keygen , 26.3 , 26.3 , 26.3 ecdh , 62.5 , 62.5 , 62.6 schnorrsig_sign , 27.2 , 27.3 , 27.4 schnorrsig_verify , 64.1 , 64.2 , 64.2 ellswift_encode , 22.1 , 22.2 , 22.3 ellswift_decode , 10.6 , 10.7 , 10.7 ellswift_keygen , 48.4 , 48.4 , 48.6 ellswift_ecdh , 66.2 , 66.3 , 66.4 ``` </details> ACKs for top commit: real-or-random: utACKe217ead5c4theStack: ACKe217ead5c4Tree-SHA512: 9a92a8d8682e5fd1ac40cab097770741fb78fc6de7c54edf24bd0796bc06b1ec1c375c996099fdaf228a56788208faa6aae107a53b15d596d19f047a5f1a30e6
This commit is contained in:
+8
-32
@@ -303,38 +303,14 @@ static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char
|
||||
|
||||
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
|
||||
static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
|
||||
r[0] = (a->n[9] >> 14) & 0xff;
|
||||
r[1] = (a->n[9] >> 6) & 0xff;
|
||||
r[2] = ((a->n[9] & 0x3F) << 2) | ((a->n[8] >> 24) & 0x3);
|
||||
r[3] = (a->n[8] >> 16) & 0xff;
|
||||
r[4] = (a->n[8] >> 8) & 0xff;
|
||||
r[5] = a->n[8] & 0xff;
|
||||
r[6] = (a->n[7] >> 18) & 0xff;
|
||||
r[7] = (a->n[7] >> 10) & 0xff;
|
||||
r[8] = (a->n[7] >> 2) & 0xff;
|
||||
r[9] = ((a->n[7] & 0x3) << 6) | ((a->n[6] >> 20) & 0x3f);
|
||||
r[10] = (a->n[6] >> 12) & 0xff;
|
||||
r[11] = (a->n[6] >> 4) & 0xff;
|
||||
r[12] = ((a->n[6] & 0xf) << 4) | ((a->n[5] >> 22) & 0xf);
|
||||
r[13] = (a->n[5] >> 14) & 0xff;
|
||||
r[14] = (a->n[5] >> 6) & 0xff;
|
||||
r[15] = ((a->n[5] & 0x3f) << 2) | ((a->n[4] >> 24) & 0x3);
|
||||
r[16] = (a->n[4] >> 16) & 0xff;
|
||||
r[17] = (a->n[4] >> 8) & 0xff;
|
||||
r[18] = a->n[4] & 0xff;
|
||||
r[19] = (a->n[3] >> 18) & 0xff;
|
||||
r[20] = (a->n[3] >> 10) & 0xff;
|
||||
r[21] = (a->n[3] >> 2) & 0xff;
|
||||
r[22] = ((a->n[3] & 0x3) << 6) | ((a->n[2] >> 20) & 0x3f);
|
||||
r[23] = (a->n[2] >> 12) & 0xff;
|
||||
r[24] = (a->n[2] >> 4) & 0xff;
|
||||
r[25] = ((a->n[2] & 0xf) << 4) | ((a->n[1] >> 22) & 0xf);
|
||||
r[26] = (a->n[1] >> 14) & 0xff;
|
||||
r[27] = (a->n[1] >> 6) & 0xff;
|
||||
r[28] = ((a->n[1] & 0x3f) << 2) | ((a->n[0] >> 24) & 0x3);
|
||||
r[29] = (a->n[0] >> 16) & 0xff;
|
||||
r[30] = (a->n[0] >> 8) & 0xff;
|
||||
r[31] = a->n[0] & 0xff;
|
||||
secp256k1_write_be32(&r[0], (a->n[9] << 10) | (a->n[8] >> 16));
|
||||
secp256k1_write_be32(&r[4], (a->n[8] << 16) | (a->n[7] >> 10));
|
||||
secp256k1_write_be32(&r[8], (a->n[7] << 22) | (a->n[6] >> 4));
|
||||
secp256k1_write_be32(&r[12], (a->n[6] << 28) | (a->n[5] << 2) | (a->n[4] >> 24));
|
||||
secp256k1_write_be32(&r[16], (a->n[4] << 8) | (a->n[3] >> 18));
|
||||
secp256k1_write_be32(&r[20], (a->n[3] << 14) | (a->n[2] >> 12));
|
||||
secp256k1_write_be32(&r[24], (a->n[2] << 20) | (a->n[1] >> 6));
|
||||
secp256k1_write_be32(&r[28], (a->n[1] << 26) | a->n[0]);
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) {
|
||||
|
||||
+4
-32
@@ -269,38 +269,10 @@ static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char
|
||||
|
||||
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
|
||||
static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
|
||||
r[0] = (a->n[4] >> 40) & 0xFF;
|
||||
r[1] = (a->n[4] >> 32) & 0xFF;
|
||||
r[2] = (a->n[4] >> 24) & 0xFF;
|
||||
r[3] = (a->n[4] >> 16) & 0xFF;
|
||||
r[4] = (a->n[4] >> 8) & 0xFF;
|
||||
r[5] = a->n[4] & 0xFF;
|
||||
r[6] = (a->n[3] >> 44) & 0xFF;
|
||||
r[7] = (a->n[3] >> 36) & 0xFF;
|
||||
r[8] = (a->n[3] >> 28) & 0xFF;
|
||||
r[9] = (a->n[3] >> 20) & 0xFF;
|
||||
r[10] = (a->n[3] >> 12) & 0xFF;
|
||||
r[11] = (a->n[3] >> 4) & 0xFF;
|
||||
r[12] = ((a->n[2] >> 48) & 0xF) | ((a->n[3] & 0xF) << 4);
|
||||
r[13] = (a->n[2] >> 40) & 0xFF;
|
||||
r[14] = (a->n[2] >> 32) & 0xFF;
|
||||
r[15] = (a->n[2] >> 24) & 0xFF;
|
||||
r[16] = (a->n[2] >> 16) & 0xFF;
|
||||
r[17] = (a->n[2] >> 8) & 0xFF;
|
||||
r[18] = a->n[2] & 0xFF;
|
||||
r[19] = (a->n[1] >> 44) & 0xFF;
|
||||
r[20] = (a->n[1] >> 36) & 0xFF;
|
||||
r[21] = (a->n[1] >> 28) & 0xFF;
|
||||
r[22] = (a->n[1] >> 20) & 0xFF;
|
||||
r[23] = (a->n[1] >> 12) & 0xFF;
|
||||
r[24] = (a->n[1] >> 4) & 0xFF;
|
||||
r[25] = ((a->n[0] >> 48) & 0xF) | ((a->n[1] & 0xF) << 4);
|
||||
r[26] = (a->n[0] >> 40) & 0xFF;
|
||||
r[27] = (a->n[0] >> 32) & 0xFF;
|
||||
r[28] = (a->n[0] >> 24) & 0xFF;
|
||||
r[29] = (a->n[0] >> 16) & 0xFF;
|
||||
r[30] = (a->n[0] >> 8) & 0xFF;
|
||||
r[31] = a->n[0] & 0xFF;
|
||||
secp256k1_write_be64(&r[0], (a->n[4] << 16) | (a->n[3] >> 36));
|
||||
secp256k1_write_be64(&r[8], (a->n[3] << 28) | (a->n[2] >> 24));
|
||||
secp256k1_write_be64(&r[16], (a->n[2] << 40) | (a->n[1] >> 12));
|
||||
secp256k1_write_be64(&r[24], (a->n[1] << 52) | a->n[0]);
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) {
|
||||
|
||||
Reference in New Issue
Block a user