Last active
March 10, 2023 10:15
-
-
Save aalness/577c7a6da1e669a07db9 to your computer and use it in GitHub Desktop.
Benchmark SHA256 for libsecp256k1 / crypto++ / openssl
This file contains hidden or 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
#include <string.h> | |
#include <sys/time.h> | |
#include <iostream> | |
#include <openssl/sha.h> | |
#include <openssl/rand.h> | |
#include "secp256k1/src/hash_impl.h" | |
#include "cryptopp/sha.h" | |
static const size_t NUM_RUNS = 300000UL; | |
static double msecs(void) | |
{ | |
struct timeval te; | |
gettimeofday(&te, NULL); | |
double msecs = te.tv_sec*1000.0 + te.tv_usec/1000.0; | |
return msecs; | |
} | |
static void openssl(const std::string& message, unsigned char* out) | |
{ | |
SHA256_CTX sha256; | |
SHA256_Init(&sha256); | |
SHA256_Update(&sha256, message.c_str(), message.length()); | |
SHA256_Final(out, &sha256); | |
} | |
static void cryptopp(const std::string& message, unsigned char* out) | |
{ | |
CryptoPP::SHA256 hash; | |
hash.Update((const byte*)message.c_str(), message.length()); | |
hash.Final(out); | |
} | |
static void secp256k1(const std::string& message, unsigned char* out) | |
{ | |
secp256k1_sha256_t hasher; | |
secp256k1_sha256_initialize(&hasher); | |
secp256k1_sha256_write(&hasher, (const unsigned char*)message.c_str(), message.length()); | |
secp256k1_sha256_finalize(&hasher, out); | |
} | |
int main(int ac, char *av[]) | |
{ | |
if (ac != 2) | |
{ | |
std::cout << "usage: " << av[0] << " <message size>" << std::endl; | |
return 1; | |
} | |
const size_t size = strtoul(av[1], 0, 0); | |
assert(size); | |
std::cout << "size: " << size << ", num runs: " << NUM_RUNS << std::endl; | |
// test correctness | |
for (size_t i = 0; i < 1000; ++i) | |
{ | |
std::string message; | |
message.resize(size); | |
assert(RAND_bytes((unsigned char*)&message[0], size) == 1); | |
unsigned char out[32], out2[32], out3[32]; | |
openssl(message, out); | |
cryptopp(message, out2); | |
secp256k1(message, out3); | |
assert(!memcmp(out, out2, 32)); | |
assert(!memcmp(out, out3, 32)); | |
} | |
std::string message; | |
message.resize(size); | |
assert(RAND_bytes((unsigned char*)&message[0], size) == 1); | |
double before, after; | |
before = msecs(); | |
for (size_t i = 0; i < NUM_RUNS; ++i) | |
{ | |
unsigned char out[32]; | |
cryptopp(message, out); | |
} | |
after = msecs(); | |
std::cout << " cryptopp took: " << (after-before) << "ms" << std::endl; | |
before = msecs(); | |
for (size_t i = 0; i < NUM_RUNS; ++i) | |
{ | |
unsigned char out[32]; | |
openssl(message, out); | |
} | |
after = msecs(); | |
std::cout << " openssl took: " << (after-before) << "ms" << std::endl; | |
before = msecs(); | |
for (size_t i = 0; i < NUM_RUNS; ++i) | |
{ | |
unsigned char out[32]; | |
secp256k1(message, out); | |
} | |
after = msecs(); | |
std::cout << "libsecp256k1 took: " << (after-before) << "ms" << std::endl; | |
return 0; | |
} |
Interesting. Perhaps it's more relevant to do many full cycles (initialize + write short message + finalize), as that is the use case in libsecp256k1 - not large amounts of data.
Yup. Had a similar thought but the numbers still work out the same. Code updated. I verified that at least with the cryptopp code it is using the ASM version of SHA256 transform.
andy@lab:~$ ./benchmark 32
size: 32, num runs: 300000
cryptopp took: 286.192ms
openssl took: 162.157ms
libsecp256k1 took: 352.647ms
andy@lab:~$ ./benchmark 256
size: 256, num runs: 300000
cryptopp took: 900.827ms
openssl took: 697.887ms
libsecp256k1 took: 1616.3ms
andy@lab:~$ ./benchmark 2048
size: 2048, num runs: 300000
cryptopp took: 5015.51ms
openssl took: 4536.79ms
libsecp256k1 took: 10477.9ms
andy@lab:~$ ./benchmark 8192
size: 8192, num runs: 300000
cryptopp took: 19195ms
openssl took: 17554.8ms
libsecp256k1 took: 40710.7ms
I don't think these #s are particularly damning or anything. For the normal workload of libsecp256k1 (on order of 32 bytes) it is perfectly fine. At least it would be remarkable if the difference mattered but we'd have much bigger problems to deal with in that case.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A few runs:
The openssl numbers seem roughly consistent w/
openssl speed sha256