Botan  1.11.15
src/lib/misc/benchmark/benchmark.cpp
Go to the documentation of this file.
00001 /*
00002 * Runtime benchmarking
00003 * (C) 2008-2009,2013 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/benchmark.h>
00009 #include <botan/internal/algo_registry.h>
00010 #include <botan/buf_comp.h>
00011 #include <botan/cipher_mode.h>
00012 #include <botan/block_cipher.h>
00013 #include <botan/stream_cipher.h>
00014 #include <botan/hash.h>
00015 #include <botan/mac.h>
00016 #include <vector>
00017 #include <chrono>
00018 
00019 namespace Botan {
00020 
00021 namespace {
00022 
00023 double time_op(std::chrono::nanoseconds runtime, std::function<void ()> op)
00024    {
00025    std::chrono::nanoseconds time_used(0);
00026    size_t reps = 0;
00027 
00028    auto start = std::chrono::high_resolution_clock::now();
00029 
00030    while(time_used < runtime)
00031       {
00032       op();
00033       ++reps;
00034       time_used = std::chrono::high_resolution_clock::now() - start;
00035       }
00036 
00037    const u64bit nsec_used = std::chrono::duration_cast<std::chrono::nanoseconds>(time_used).count();
00038 
00039    const double seconds_used = static_cast<double>(nsec_used) / 1000000000;
00040 
00041    return reps / seconds_used; // ie, return ops per second
00042    }
00043 
00044 std::map<std::string, double>
00045 time_algorithm_ops(const std::string& name,
00046                    const std::string& provider,
00047                    RandomNumberGenerator& rng,
00048                    std::chrono::nanoseconds runtime,
00049                    size_t buf_size)
00050    {
00051    const size_t Mebibyte = 1024*1024;
00052 
00053    secure_vector<byte> buffer(buf_size * 1024);
00054    rng.randomize(&buffer[0], buffer.size());
00055 
00056    const double mb_mult = buffer.size() / static_cast<double>(Mebibyte);
00057 
00058    if(BlockCipher* p = make_a<BlockCipher>(name, provider))
00059       {
00060       std::unique_ptr<BlockCipher> bc(p);
00061 
00062       const SymmetricKey key(rng, bc->maximum_keylength());
00063 
00064       return std::map<std::string, double>({
00065             { "key schedule", time_op(runtime / 8, [&]() { bc->set_key(key); }) },
00066             { "encrypt", mb_mult * time_op(runtime / 2, [&]() { bc->encrypt(buffer); }) },
00067             { "decrypt", mb_mult * time_op(runtime / 2, [&]() { bc->decrypt(buffer); }) },
00068          });
00069       }
00070    else if(StreamCipher* p = make_a<StreamCipher>(name, provider))
00071       {
00072       std::unique_ptr<StreamCipher> sc(p);
00073 
00074       const SymmetricKey key(rng, sc->maximum_keylength());
00075 
00076       return std::map<std::string, double>({
00077             { "key schedule", time_op(runtime / 8, [&]() { sc->set_key(key); }) },
00078             { "", mb_mult * time_op(runtime, [&]() { sc->encipher(buffer); }) },
00079          });
00080       }
00081    else if(HashFunction* p = make_a<HashFunction>(name, provider))
00082       {
00083       std::unique_ptr<HashFunction> h(p);
00084 
00085       return std::map<std::string, double>({
00086             { "", mb_mult * time_op(runtime, [&]() { h->update(buffer); }) },
00087          });
00088       }
00089    else if(MessageAuthenticationCode* p = make_a<MessageAuthenticationCode>(name, provider))
00090       {
00091       std::unique_ptr<MessageAuthenticationCode> mac(p);
00092 
00093       const SymmetricKey key(rng, mac->maximum_keylength());
00094 
00095       return std::map<std::string, double>({
00096             { "key schedule", time_op(runtime / 8, [&]() { mac->set_key(key); }) },
00097             { "", mb_mult * time_op(runtime, [&]() { mac->update(buffer); }) },
00098          });
00099       }
00100    else
00101       {
00102       std::unique_ptr<Cipher_Mode> enc(get_cipher_mode(name, ENCRYPTION));
00103       std::unique_ptr<Cipher_Mode> dec(get_cipher_mode(name, DECRYPTION));
00104 
00105       if(enc && dec)
00106          {
00107          const SymmetricKey key(rng, enc->key_spec().maximum_keylength());
00108 
00109          return std::map<std::string, double>({
00110                { "key schedule", time_op(runtime / 4, [&]() { enc->set_key(key); dec->set_key(key); }) / 2 },
00111                { "encrypt", mb_mult * time_op(runtime / 2, [&]() { enc->update(buffer, 0); buffer.resize(buf_size*1024); }) },
00112                { "decrypt", mb_mult * time_op(runtime / 2, [&]() { dec->update(buffer, 0); buffer.resize(buf_size*1024); }) },
00113             });
00114          }
00115       }
00116 
00117    return std::map<std::string, double>();
00118    }
00119 
00120 double find_first_in(const std::map<std::string, double>& m,
00121                      const std::vector<std::string>& keys)
00122    {
00123    for(auto key : keys)
00124       {
00125       auto i = m.find(key);
00126       if(i != m.end())
00127          return i->second;
00128       }
00129 
00130    throw std::runtime_error("In algo benchmark no usable keys found in result");
00131    }
00132 
00133 std::set<std::string> get_all_providers_of(const std::string& algo)
00134    {
00135    std::set<std::string> provs;
00136 
00137    auto add_to_set = [&provs](const std::vector<std::string>& str) { for(auto&& s : str) { provs.insert(s); } };
00138 
00139    add_to_set(Algo_Registry<BlockCipher>::global_registry().providers_of(algo));
00140    add_to_set(Algo_Registry<StreamCipher>::global_registry().providers_of(algo));
00141    add_to_set(Algo_Registry<HashFunction>::global_registry().providers_of(algo));
00142    add_to_set(Algo_Registry<MessageAuthenticationCode>::global_registry().providers_of(algo));
00143 
00144    return provs;
00145    }
00146 
00147 }
00148 
00149 std::map<std::string, double>
00150 algorithm_benchmark(const std::string& name,
00151                     RandomNumberGenerator& rng,
00152                     std::chrono::milliseconds milliseconds,
00153                     size_t buf_size)
00154    {
00155    //Algorithm_Factory& af = global_state().algorithm_factory();
00156    const auto providers = get_all_providers_of(name);
00157 
00158    std::map<std::string, double> all_results; // provider -> ops/sec
00159 
00160    if(!providers.empty())
00161       {
00162       const std::chrono::nanoseconds ns_per_provider = milliseconds / providers.size();
00163 
00164       for(auto provider : providers)
00165          {
00166          auto results = time_algorithm_ops(name, provider, rng, ns_per_provider, buf_size);
00167          all_results[provider] = find_first_in(results, { "", "update", "encrypt" });
00168          }
00169       }
00170 
00171    return all_results;
00172    }
00173 
00174 }