diff options
author | lloyd <[email protected]> | 2014-01-10 00:08:13 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-01-10 00:08:13 +0000 |
commit | 57789bdfc55061002b2727d0b32587612829a37c (patch) | |
tree | 99f36631b4ec50c5187a1b0a7c256b99182373ad /src/cmd/speed | |
parent | 94968c917407a63d888fd3eb4d02491f60de6ebc (diff) |
Split up test vectors into per-algo files and app into botan-test for
the tests and botan for everything else.
Diffstat (limited to 'src/cmd/speed')
-rw-r--r-- | src/cmd/speed/pk_bench.cpp | 755 | ||||
-rw-r--r-- | src/cmd/speed/speed.cpp | 229 | ||||
-rw-r--r-- | src/cmd/speed/speed.h | 25 | ||||
-rw-r--r-- | src/cmd/speed/timer.cpp | 64 | ||||
-rw-r--r-- | src/cmd/speed/timer.h | 50 |
5 files changed, 1123 insertions, 0 deletions
diff --git a/src/cmd/speed/pk_bench.cpp b/src/cmd/speed/pk_bench.cpp new file mode 100644 index 000000000..6ebb366c5 --- /dev/null +++ b/src/cmd/speed/pk_bench.cpp @@ -0,0 +1,755 @@ +/* +* (C) 2009-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "speed.h" +#include "timer.h" + +#include <botan/pkcs8.h> +#include <botan/mem_ops.h> +#include <botan/parsing.h> +#include <botan/oids.h> +#include <map> + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include <botan/x509_key.h> + #include <botan/pkcs8.h> + #include <botan/pubkey.h> +#endif + +#if defined(BOTAN_HAS_RSA) + #include <botan/rsa.h> +#endif + +#if defined(BOTAN_HAS_DSA) + #include <botan/dsa.h> +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + #include <botan/dh.h> +#endif + +#if defined(BOTAN_HAS_NYBERG_RUEPPEL) + #include <botan/nr.h> +#endif + +#if defined(BOTAN_HAS_RW) + #include <botan/rw.h> +#endif + +#if defined(BOTAN_HAS_ELGAMAL) + #include <botan/elgamal.h> +#endif + +#if defined(BOTAN_HAS_DLIES) + #include <botan/dlies.h> + #include <botan/kdf2.h> + #include <botan/hmac.h> + #include <botan/sha160.h> +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include <botan/ecdsa.h> +#endif + +#if defined(BOTAN_HAS_ECDH) + #include <botan/ecdh.h> +#endif + +#if defined(BOTAN_HAS_GOST_34_10_2001) + #include <botan/gost_3410.h> +#endif + +using namespace Botan; + +#include <iostream> +#include <fstream> +#include <string> +#include <memory> +#include <set> + +#define BENCH_FAULT_PROT DISABLE_FAULT_PROTECTION +//#define BENCH_FAULT_PROT ENABLE_FAULT_PROTECTION + +namespace { + +const char* ec_domains[] = { + "secp160r2", + "secp192r1", + "secp224r1", + "secp256r1", + "secp384r1", + "secp521r1", + nullptr +}; + +class Benchmark_Report + { + public: + void report(const std::string& name, Timer timer) + { + std::cout << name << " " << timer << std::endl; + data[name].insert(timer); + } + + private: + std::map<std::string, std::set<Timer> > data; + }; + +void benchmark_enc_dec(PK_Encryptor& enc, PK_Decryptor& dec, + Timer& enc_timer, Timer& dec_timer, + RandomNumberGenerator& rng, + u32bit runs, double seconds) + { + std::vector<byte> plaintext, ciphertext; + + for(u32bit i = 0; i != runs; ++i) + { + if(enc_timer.seconds() < seconds || ciphertext.size() == 0) + { + plaintext.resize(enc.maximum_input_size()); + + // Ensure for Raw, etc, it stays large + if((i % 100) == 0) + { + rng.randomize(&plaintext[0], plaintext.size()); + plaintext[0] |= 0x80; + } + + enc_timer.start(); + ciphertext = enc.encrypt(plaintext, rng); + enc_timer.stop(); + } + + if(dec_timer.seconds() < seconds) + { + dec_timer.start(); + std::vector<byte> plaintext_out = unlock(dec.decrypt(ciphertext)); + dec_timer.stop(); + + if(plaintext_out != plaintext) + { // has never happened... + std::cerr << "Contents mismatched on decryption during benchmark!\n"; + } + } + } + } + +void benchmark_sig_ver(PK_Verifier& ver, PK_Signer& sig, + Timer& verify_timer, Timer& sig_timer, + RandomNumberGenerator& rng, + u32bit runs, double seconds) + { + std::vector<byte> message, signature, sig_random; + + for(u32bit i = 0; i != runs; ++i) + { + if(sig_timer.seconds() < seconds || signature.size() == 0) + { + if((i % 100) == 0) + { + message.resize(48); + rng.randomize(&message[0], message.size()); + } + + sig_timer.start(); + signature = sig.sign_message(message, rng); + sig_timer.stop(); + } + + if(verify_timer.seconds() < seconds) + { + verify_timer.start(); + const bool verified = ver.verify_message(message, signature); + verify_timer.stop(); + + if(!verified) + std::cerr << "Signature verification failure\n"; + + if((i % 100) == 0) + { + sig_random = unlock(rng.random_vec(signature.size())); + + verify_timer.start(); + const bool verified_bad = ver.verify_message(message, sig_random); + verify_timer.stop(); + + if(verified_bad) + std::cerr << "Signature verification failure (bad sig OK)\n"; + } + } + } + } + +/* + Between benchmark_rsa_rw + benchmark_dsa_nr: + Type of the key + Arguments to the constructor (A list of some arbitrary type?) + Type of padding +*/ + +#if defined(BOTAN_HAS_RSA) +void benchmark_rsa(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + + size_t keylens[] = { 1024, 2048, 4096, 6144, 0 }; + + for(size_t i = 0; keylens[i]; ++i) + { + size_t keylen = keylens[i]; + + //const std::string sig_padding = "EMSA4(SHA-1)"; + //const std::string enc_padding = "EME1(SHA-1)"; + const std::string sig_padding = "EMSA-PKCS1-v1_5(SHA-1)"; + const std::string enc_padding = "EME-PKCS1-v1_5"; + + Timer keygen_timer("keygen"); + Timer verify_timer(sig_padding + " verify"); + Timer sig_timer(sig_padding + " signature"); + Timer enc_timer(enc_padding + " encrypt"); + Timer dec_timer(enc_padding + " decrypt"); + + try + { + +#if 0 + // for profiling + PKCS8_PrivateKey* pkcs8_key = + PKCS8::load_key("rsa/" + to_string(keylen) + ".pem", rng); + RSA_PrivateKey* key_ptr = dynamic_cast<RSA_PrivateKey*>(pkcs8_key); + + RSA_PrivateKey key = *key_ptr; +#else + keygen_timer.start(); + RSA_PrivateKey key(rng, keylen); + keygen_timer.stop(); +#endif + + while(verify_timer.seconds() < seconds || + sig_timer.seconds() < seconds) + { + PK_Encryptor_EME enc(key, enc_padding); + PK_Decryptor_EME dec(key, enc_padding); + + benchmark_enc_dec(enc, dec, enc_timer, dec_timer, + rng, 10000, seconds); + + PK_Signer sig(key, sig_padding); + PK_Verifier ver(key, sig_padding); + + benchmark_sig_ver(ver, sig, verify_timer, + sig_timer, rng, 10000, seconds); + } + + const std::string rsa_keylen = "RSA-" + std::to_string(keylen); + + report.report(rsa_keylen, keygen_timer); + report.report(rsa_keylen, verify_timer); + report.report(rsa_keylen, sig_timer); + report.report(rsa_keylen, enc_timer); + report.report(rsa_keylen, dec_timer); + } + catch(Stream_IO_Error) + { + } + catch(Exception& e) + { + std::cout << e.what() << "\n"; + } + } + + } +#endif + +#if defined(BOTAN_HAS_RW) +void benchmark_rw(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + + const u32bit keylens[] = { 1024, 2048, 4096, 6144, 0 }; + + for(size_t j = 0; keylens[j]; j++) + { + u32bit keylen = keylens[j]; + + std::string padding = "EMSA2(SHA-256)"; + + Timer keygen_timer("keygen"); + Timer verify_timer(padding + " verify"); + Timer sig_timer(padding + " signature"); + + while(verify_timer.seconds() < seconds || + sig_timer.seconds() < seconds) + { + keygen_timer.start(); + RW_PrivateKey key(rng, keylen); + keygen_timer.stop(); + + PK_Signer sig(key, padding); + PK_Verifier ver(key, padding); + + benchmark_sig_ver(ver, sig, verify_timer, sig_timer, + rng, 10000, seconds); + } + + const std::string nm = "RW-" + std::to_string(keylen); + report.report(nm, keygen_timer); + report.report(nm, verify_timer); + report.report(nm, sig_timer); + } + } +#endif + +#if defined(BOTAN_HAS_ECDSA) + +void benchmark_ecdsa(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + for(size_t j = 0; ec_domains[j]; j++) + { + EC_Group params(ec_domains[j]); + + const size_t pbits = params.get_curve().get_p().bits(); + + size_t hashbits = pbits; + + if(hashbits <= 192) + hashbits = 160; + if(hashbits == 521) + hashbits = 512; + + const std::string padding = "EMSA1(SHA-" + std::to_string(hashbits) + ")"; + + Timer keygen_timer("keygen"); + Timer verify_timer(padding + " verify"); + Timer sig_timer(padding + " signature"); + + while(verify_timer.seconds() < seconds || + sig_timer.seconds() < seconds) + { + keygen_timer.start(); + ECDSA_PrivateKey key(rng, params); + keygen_timer.stop(); + + PK_Signer sig(key, padding, IEEE_1363, BENCH_FAULT_PROT); + PK_Verifier ver(key, padding); + + benchmark_sig_ver(ver, sig, verify_timer, + sig_timer, rng, 1000, seconds); + } + + const std::string nm = "ECDSA-" + std::to_string(pbits); + + report.report(nm, keygen_timer); + report.report(nm, verify_timer); + report.report(nm, sig_timer); + } + } + +#endif + +#if defined(BOTAN_HAS_GOST_34_10_2001) + +void benchmark_gost_3410(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + for(size_t j = 0; ec_domains[j]; j++) + { + EC_Group params(ec_domains[j]); + + const size_t pbits = params.get_curve().get_p().bits(); + + const std::string padding = "EMSA1(GOST-34.11)"; + + Timer keygen_timer("keygen"); + Timer verify_timer(padding + " verify"); + Timer sig_timer(padding + " signature"); + + while(verify_timer.seconds() < seconds || + sig_timer.seconds() < seconds) + { + keygen_timer.start(); + GOST_3410_PrivateKey key(rng, params); + keygen_timer.stop(); + + PK_Signer sig(key, padding, IEEE_1363, BENCH_FAULT_PROT); + PK_Verifier ver(key, padding); + + benchmark_sig_ver(ver, sig, verify_timer, + sig_timer, rng, 1000, seconds); + } + + const std::string nm = "GOST-34.10-" + std::to_string(pbits); + + report.report(nm, keygen_timer); + report.report(nm, verify_timer); + report.report(nm, sig_timer); + } + } + +#endif + +#if defined(BOTAN_HAS_ECDH) + +void benchmark_ecdh(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + for(size_t j = 0; ec_domains[j]; j++) + { + EC_Group params(ec_domains[j]); + + size_t pbits = params.get_curve().get_p().bits(); + + Timer keygen_timer("keygen"); + Timer kex_timer("key exchange"); + + while(kex_timer.seconds() < seconds) + { + keygen_timer.start(); + ECDH_PrivateKey ecdh1(rng, params); + keygen_timer.stop(); + + keygen_timer.start(); + ECDH_PrivateKey ecdh2(rng, params); + keygen_timer.stop(); + + PK_Key_Agreement ka1(ecdh1, "KDF2(SHA-1)"); + PK_Key_Agreement ka2(ecdh2, "KDF2(SHA-1)"); + + SymmetricKey secret1, secret2; + + for(size_t i = 0; i != 1000; ++i) + { + if(kex_timer.seconds() > seconds) + break; + + kex_timer.start(); + secret1 = ka1.derive_key(32, ecdh2.public_value()); + kex_timer.stop(); + + kex_timer.start(); + secret2 = ka2.derive_key(32, ecdh1.public_value()); + kex_timer.stop(); + + if(secret1 != secret2) + std::cerr << "ECDH secrets did not match\n"; + } + } + + const std::string nm = "ECDH-" + std::to_string(pbits); + report.report(nm, keygen_timer); + report.report(nm, kex_timer); + } + } + +#endif + +template<typename PRIV_KEY_TYPE> +void benchmark_dsa_nr(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { +#if defined(BOTAN_HAS_NYBERG_RUEPPEL) || defined(BOTAN_HAS_DSA) + const char* domains[] = { "dsa/jce/1024", + "dsa/botan/2048", + "dsa/botan/3072", + nullptr }; + + std::string algo_name; + + for(size_t j = 0; domains[j]; j++) + { + size_t pbits = to_u32bit(split_on(domains[j], '/')[2]); + size_t qbits = (pbits <= 1024) ? 160 : 256; + + const std::string padding = "EMSA1(SHA-" + std::to_string(qbits) + ")"; + + Timer keygen_timer("keygen"); + Timer verify_timer(padding + " verify"); + Timer sig_timer(padding + " signature"); + + while(verify_timer.seconds() < seconds || + sig_timer.seconds() < seconds) + { + DL_Group group(domains[j]); + + keygen_timer.start(); + PRIV_KEY_TYPE key(rng, group); + algo_name = key.algo_name(); + keygen_timer.stop(); + + PK_Signer sig(key, padding, IEEE_1363, BENCH_FAULT_PROT); + PK_Verifier ver(key, padding); + + benchmark_sig_ver(ver, sig, verify_timer, + sig_timer, rng, 1000, seconds); + } + + const std::string nm = algo_name + "-" + std::to_string(pbits); + report.report(nm, keygen_timer); + report.report(nm, verify_timer); + report.report(nm, sig_timer); + } +#endif + } + +#ifdef BOTAN_HAS_DIFFIE_HELLMAN +void benchmark_dh(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + const char* domains[] = { "modp/ietf/1024", + "modp/ietf/2048", + "modp/ietf/3072", + "modp/ietf/4096", + "modp/ietf/6144", + "modp/ietf/8192", + nullptr }; + + for(size_t j = 0; domains[j]; j++) + { + Timer keygen_timer("keygen"); + Timer kex_timer("key exchange"); + + while(kex_timer.seconds() < seconds) + { + DL_Group group(domains[j]); + + keygen_timer.start(); + DH_PrivateKey dh1(rng, group); + keygen_timer.stop(); + + keygen_timer.start(); + DH_PrivateKey dh2(rng, group); + keygen_timer.stop(); + + PK_Key_Agreement ka1(dh1, "KDF2(SHA-1)"); + PK_Key_Agreement ka2(dh2, "KDF2(SHA-1)"); + + SymmetricKey secret1, secret2; + + for(size_t i = 0; i != 1000; ++i) + { + if(kex_timer.seconds() > seconds) + break; + + kex_timer.start(); + secret1 = ka1.derive_key(32, dh2.public_value()); + kex_timer.stop(); + + kex_timer.start(); + secret2 = ka2.derive_key(32, dh1.public_value()); + kex_timer.stop(); + + if(secret1 != secret2) + std::cerr << "DH secrets did not match\n"; + } + } + + const std::string nm = "DH-" + split_on(domains[j], '/')[2]; + report.report(nm, keygen_timer); + report.report(nm, kex_timer); + } + } +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_DLIES) +void benchmark_dlies(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + const char* domains[] = { "modp/ietf/768", + "modp/ietf/1024", + "modp/ietf/2048", + "modp/ietf/3072", + "modp/ietf/4096", + "modp/ietf/6144", + "modp/ietf/8192", + nullptr }; + + for(size_t j = 0; domains[j]; j++) + { + Timer keygen_timer("keygen"); + Timer kex_timer("key exchange"); + + Timer enc_timer("encrypt"); + Timer dec_timer("decrypt"); + + while(enc_timer.seconds() < seconds || dec_timer.seconds() < seconds) + { + DL_Group group(domains[j]); + + keygen_timer.start(); + DH_PrivateKey dh1_priv(rng, group); + keygen_timer.stop(); + + keygen_timer.start(); + DH_PrivateKey dh2_priv(rng, group); + keygen_timer.stop(); + + DH_PublicKey dh2_pub(dh2_priv); + + DLIES_Encryptor dlies_enc(dh1_priv, + new KDF2(new SHA_160), + new HMAC(new SHA_160)); + + dlies_enc.set_other_key(dh2_pub.public_value()); + + DLIES_Decryptor dlies_dec(dh2_priv, + new KDF2(new SHA_160), + new HMAC(new SHA_160)); + + benchmark_enc_dec(dlies_enc, dlies_dec, + enc_timer, dec_timer, rng, + 1000, seconds); + } + + const std::string nm = "DLIES-" + split_on(domains[j], '/')[2]; + report.report(nm, keygen_timer); + report.report(nm, enc_timer); + report.report(nm, dec_timer); + } + } +#endif + +#ifdef BOTAN_HAS_ELGAMAL +void benchmark_elg(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + const char* domains[] = { "modp/ietf/768", + "modp/ietf/1024", + "modp/ietf/2048", + "modp/ietf/3072", + "modp/ietf/4096", + "modp/ietf/6144", + "modp/ietf/8192", + nullptr }; + + const std::string algo_name = "ElGamal"; + + for(size_t j = 0; domains[j]; j++) + { + size_t pbits = to_u32bit(split_on(domains[j], '/')[2]); + + const std::string padding = "EME1(SHA-1)"; + + Timer keygen_timer("keygen"); + Timer enc_timer(padding + " encrypt"); + Timer dec_timer(padding + " decrypt"); + + while(enc_timer.seconds() < seconds || + dec_timer.seconds() < seconds) + { + DL_Group group(domains[j]); + + keygen_timer.start(); + ElGamal_PrivateKey key(rng, group); + keygen_timer.stop(); + + PK_Decryptor_EME dec(key, padding); + PK_Encryptor_EME enc(key, padding); + + benchmark_enc_dec(enc, dec, enc_timer, dec_timer, + rng, 1000, seconds); + } + + const std::string nm = algo_name + "-" + std::to_string(pbits); + report.report(nm, keygen_timer); + report.report(nm, enc_timer); + report.report(nm, dec_timer); + } + } +#endif + +} + +void bench_pk(RandomNumberGenerator& rng, + const std::string& algo, double seconds) + { + /* + There is some strangeness going on here. It looks like algorithms + at the end take some kind of penalty. For example, running the RW tests + first got a result of: + RW-1024: 148.14 ms / private operation + but running them last output: + RW-1024: 363.54 ms / private operation + + I think it's from memory fragmentation in the allocators, but I'm + not really sure. Need to investigate. + + Until then, I've basically ordered the tests in order of most important + algorithms (RSA, DSA) to least important (NR, RW). + + This strange behaviour does not seem to occur with DH (?) + + To get more accurate runs, use --bench-algo (RSA|DSA|DH|ELG|NR); in this + case the distortion is less than 5%, which is good enough. + + We do random keys with the DL schemes, since it's so easy and fast to + generate keys for them. For RSA and RW, we load the keys from a file. The + RSA keys are stored in a PKCS #8 structure, while RW is stored in a more + ad-hoc format (the RW algorithm has no assigned OID that I know of, so + there is no way to encode a RW key into a PKCS #8 structure). + */ + + Benchmark_Report report; + +#if defined(BOTAN_HAS_RSA) + if(algo == "All" || algo == "RSA") + benchmark_rsa(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_DSA) + if(algo == "All" || algo == "DSA") + benchmark_dsa_nr<DSA_PrivateKey>(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_ECDSA) + if(algo == "All" || algo == "ECDSA") + benchmark_ecdsa(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_ECDH) + if(algo == "All" || algo == "ECDH") + benchmark_ecdh(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_GOST_34_10_2001) + if(algo == "All" || algo == "GOST-34.10") + benchmark_gost_3410(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + if(algo == "All" || algo == "DH") + benchmark_dh(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_DLIES) + if(algo == "All" || algo == "DLIES") + benchmark_dlies(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_ELGAMAL) + if(algo == "All" || algo == "ELG" || algo == "ElGamal") + benchmark_elg(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_NYBERG_RUEPPEL) + if(algo == "All" || algo == "NR") + benchmark_dsa_nr<NR_PrivateKey>(rng, seconds, report); +#endif + +#if defined(BOTAN_HAS_RW) + if(algo == "All" || algo == "RW") + benchmark_rw(rng, seconds, report); +#endif + } diff --git a/src/cmd/speed/speed.cpp b/src/cmd/speed/speed.cpp new file mode 100644 index 000000000..2e3d2c6fa --- /dev/null +++ b/src/cmd/speed/speed.cpp @@ -0,0 +1,229 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "speed.h" +#include <iostream> +#include <iomanip> + +#include <botan/benchmark.h> +#include <botan/aead.h> +#include <botan/auto_rng.h> +#include <botan/libstate.h> +#include <botan/pipe.h> +#include <botan/filters.h> +#include <botan/engine.h> +#include <botan/parsing.h> +#include <botan/symkey.h> +#include <botan/hex.h> + +#include <chrono> + +typedef std::chrono::high_resolution_clock benchmark_clock; + + +using namespace Botan; + +namespace { + +const std::string default_benchmark_list[] = { + + /* Block ciphers */ + "AES-128", + "AES-192", + "AES-256", + "Blowfish", + "CAST-128", + "CAST-256", + "DES", + "DESX", + "GOST", + "IDEA", + "KASUMI", + "MARS", + "MISTY1", + "Noekeon", + "RC2", + "RC5(16)", + "RC6", + "SAFER-SK(10)", + "SEED", + "Serpent", + "Skipjack", + "Square", + "TEA", + "TripleDES", + "Threefish-512", + "Twofish", + "XTEA", + + /* Cipher modes */ + "AES-128/CBC", + "AES-128/CTR-BE", + "AES-128/EAX", + "AES-128/OCB", + "AES-128/GCM", + "AES-128/XTS", + + "Serpent/CBC", + "Serpent/CTR-BE", + "Serpent/EAX", + "Serpent/OCB", + "Serpent/GCM", + "Serpent/XTS", + + /* Stream ciphers */ + "RC4", + "Salsa20", + + /* Hashes */ + "HAS-160", + "Keccak-1600(512)", + "MD4", + "MD5", + "RIPEMD-128", + "RIPEMD-160", + "SHA-160", + "SHA-256", + "SHA-384", + "SHA-512", + "Skein-512", + "Tiger", + "Whirlpool", + + /* MACs */ + "CMAC(AES-128)", + "HMAC(SHA-1)", + "", +}; + +void report_results(const std::string& algo, + const std::map<std::string, double>& speeds) + { + if(speeds.empty()) + return; + + // invert, showing fastest impl first + std::map<double, std::string> results; + + for(auto i = speeds.begin(); i != speeds.end(); ++i) + { + // Speeds might collide, tweak slightly to handle this + if(results[i->second] == "") + results[i->second] = i->first; + else + results[i->second - .01] = i->first; + } + + std::cout << algo; + + for(auto i = results.rbegin(); i != results.rend(); ++i) + { + std::cout << " [" << i->second << "] " + << std::fixed << std::setprecision(2) << i->first; + } + std::cout << std::endl; + } + +void time_transform(std::unique_ptr<Transformation> tf, + RandomNumberGenerator& rng) + { + if(!tf) + return; + + if(tf->maximum_keylength() > 0) + tf->set_key(rng.random_vec(tf->maximum_keylength())); + + for(size_t buf_size : { 16, 64, 256, 1024, 8192 }) + { + secure_vector<byte> buffer(buf_size); + + double res = time_op(std::chrono::seconds(1), + [&tf,&buffer,buf_size,&rng]{ + tf->start_vec(rng.random_vec(tf->default_nonce_length())); + tf->finish(buffer); + buffer.resize(buf_size); + }); + + const double Mbytes = (res * buf_size) / 1024 / 1024; + + std::cout << tf->name() << " " << std::setprecision(4) << Mbytes + << " MiB / sec with " << buf_size << " byte blocks\n"; + } + } + +void time_transform(const std::string& algo, RandomNumberGenerator& rng) + { + std::unique_ptr<Transformation> tf; + tf.reset(get_aead(algo, ENCRYPTION)); + time_transform(std::move(tf), rng); + } + +} + +void bench_algo(const std::string& algo, + RandomNumberGenerator& rng, + double seconds, + size_t buf_size) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + + std::chrono::milliseconds ms( + static_cast<std::chrono::milliseconds::rep>(seconds * 1000)); + + std::map<std::string, double> speeds = algorithm_benchmark(algo, af, rng, ms, buf_size); + + report_results(algo, speeds); + + if(speeds.empty()) + time_transform(algo, rng); + + if(speeds.empty()) + bench_pk(rng, algo, seconds); + } + +int speed_main(int argc, char* argv[]) + { + OptionParser opts("seconds=|buf-size="); + opts.parse(argv); + + double seconds = .5; + u32bit buf_size = 16; + + if(opts.is_set("seconds")) + { + seconds = std::atof(opts.value("seconds").c_str()); + if(seconds < 0.1 || seconds > (5 * 60)) + { + std::cout << "Invalid argument to --seconds\n"; + return 2; + } + } + + if(opts.is_set("buf-size")) + { + buf_size = std::atoi(opts.value("buf-size").c_str()); + if(buf_size == 0 || buf_size > 1024) + { + std::cout << "Invalid argument to --buf-size\n"; + return 2; + } + } + + const auto args = opts.arguments(); + + if(args.empty() || args[0] == "help" || args[0] == "-h") + { + std::cout << "Help!\n"; + return 1; + } + + AutoSeeded_RNG rng; + + for(auto alg: args) + bench_algo(alg, rng, seconds, buf_size); + + return 0; + } diff --git a/src/cmd/speed/speed.h b/src/cmd/speed/speed.h new file mode 100644 index 000000000..b4aabca8a --- /dev/null +++ b/src/cmd/speed/speed.h @@ -0,0 +1,25 @@ + +#ifndef BOTAN_CHECK_BENCHMARK_H__ +#define BOTAN_CHECK_BENCHMARK_H__ + +#include "../apps.h" +#include <botan/rng.h> +#include <botan/transform.h> +#include <string> + +using namespace Botan; + +int speed_main(int argc, char* argv[]); + +void benchmark(double seconds, + size_t buf_size); + +void bench_algo(const std::string& algo_name, + RandomNumberGenerator& rng, + double seconds, + size_t buf_size); + +void bench_pk(RandomNumberGenerator& rng, + const std::string& algo, double seconds); + +#endif diff --git a/src/cmd/speed/timer.cpp b/src/cmd/speed/timer.cpp new file mode 100644 index 000000000..9f3d34607 --- /dev/null +++ b/src/cmd/speed/timer.cpp @@ -0,0 +1,64 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "timer.h" +#include <chrono> +#include <iomanip> + +Timer::Timer(const std::string& n, u32bit e_mul) : + name(n), event_mult(e_mul) + { + time_used = 0; + timer_start = 0; + event_count = 0; + } + +void Timer::start() + { + stop(); + timer_start = get_clock(); + } + +void Timer::stop() + { + if(timer_start) + { + u64bit now = get_clock(); + + if(now > timer_start) + time_used += (now - timer_start); + + timer_start = 0; + ++event_count; + } + } + +u64bit Timer::get_clock() + { + auto now = std::chrono::high_resolution_clock::now().time_since_epoch(); + return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); + } + +std::ostream& operator<<(std::ostream& out, Timer& timer) + { + //out << timer.value() << " "; + + double events_per_second_fl = + static_cast<double>(timer.events() / timer.seconds()); + + u64bit events_per_second = static_cast<u64bit>(events_per_second_fl); + + out << events_per_second << " " << timer.get_name() << " per second; "; + + std::string op_or_ops = (timer.events() == 1) ? "op" : "ops"; + + out << std::setprecision(2) << std::fixed + << timer.ms_per_event() << " ms/op" + << " (" << timer.events() << " " << op_or_ops << " in " + << timer.milliseconds() << " ms)"; + + return out; + } diff --git a/src/cmd/speed/timer.h b/src/cmd/speed/timer.h new file mode 100644 index 000000000..48d6b6805 --- /dev/null +++ b/src/cmd/speed/timer.h @@ -0,0 +1,50 @@ + +#ifndef BOTAN_BENCHMARK_TIMER_H__ +#define BOTAN_BENCHMARK_TIMER_H__ + +#include <botan/types.h> +#include <ostream> +#include <string> + +using Botan::u64bit; +using Botan::u32bit; + +class Timer + { + public: + static u64bit get_clock(); + + Timer(const std::string& name, u32bit event_mult = 1); + + void start(); + + void stop(); + + u64bit value() { stop(); return time_used; } + double seconds() { return milliseconds() / 1000.0; } + double milliseconds() { return value() / 1000000.0; } + + double ms_per_event() { return milliseconds() / events(); } + double seconds_per_event() { return seconds() / events(); } + + u64bit events() const { return event_count * event_mult; } + std::string get_name() const { return name; } + private: + std::string name; + u64bit time_used, timer_start; + u64bit event_count, event_mult; + }; + +inline bool operator<(const Timer& x, const Timer& y) + { + return (x.get_name() < y.get_name()); + } + +inline bool operator==(const Timer& x, const Timer& y) + { + return (x.get_name() == y.get_name()); + } + +std::ostream& operator<<(std::ostream&, Timer&); + +#endif |