diff options
author | Jack Lloyd <[email protected]> | 2017-02-02 22:44:39 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-02-02 22:44:39 -0500 |
commit | 2a7127fea6e7d987432de907165eaadf30cb3cbc (patch) | |
tree | a7eb44566f8aa9dd6c83b5f6aabd819e0d684f16 /src | |
parent | 23a96ca126ebfddb2259386f12a00a8e783226ed (diff) |
Report cycles/byte in speed tests
Add SIMD perf test
Diffstat (limited to 'src')
-rw-r--r-- | src/cli/speed.cpp | 169 |
1 files changed, 150 insertions, 19 deletions
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 8104152a5..b6dd106b7 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -21,6 +21,8 @@ #include <botan/cipher_mode.h> #include <botan/entropy_src.h> +#include <botan/internal/os_utils.h> + #if defined(BOTAN_HAS_AUTO_SEEDING_RNG) #include <botan/auto_rng.h> #endif @@ -59,6 +61,10 @@ #include <botan/rsa.h> #endif +#if defined(BOTAN_HAS_ECC_GROUP) + #include <botan/ec_group.h> +#endif + #if defined(BOTAN_HAS_ECDSA) #include <botan/ecdsa.h> #endif @@ -96,8 +102,8 @@ #include <botan/chacha.h> #endif -#if defined(BOTAN_HAS_ECC_GROUP) - #include <botan/ec_group.h> +#if defined(BOTAN_HAS_SIMD_32) + #include <botan/internal/simd_32.h> #endif namespace Botan_CLI { @@ -107,12 +113,6 @@ namespace { class Timer { public: - static uint64_t get_clock() // returns nanoseconds with arbitrary epoch - { - auto now = std::chrono::high_resolution_clock::now().time_since_epoch(); - return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); - } - Timer(const std::string& name, uint64_t event_mult = 1) : m_name(name), m_event_mult(event_mult) {} @@ -124,13 +124,18 @@ class Timer m_doing(doing), m_event_mult(event_mult) {} - void start() { stop(); m_timer_start = get_clock(); } + void start() + { + stop(); + m_timer_start = Botan::OS::get_system_timestamp_ns(); + m_cpu_cycles_start = Botan::OS::get_processor_timestamp(); + } void stop() { if(m_timer_start) { - const uint64_t now = get_clock(); + const uint64_t now = Botan::OS::get_system_timestamp_ns(); if(now > m_timer_start) { @@ -138,6 +143,15 @@ class Timer m_time_used += dur; + if(m_cpu_cycles_start != 0) + { + uint64_t cycles_taken = Botan::OS::get_processor_timestamp() - m_cpu_cycles_start; + if(cycles_taken > 0) + { + m_cpu_cycles_used += cycles_taken; + } + } + if(m_event_count == 0) { m_min_time = m_max_time = dur; @@ -191,6 +205,8 @@ class Timer double ms_per_event() const { return milliseconds() / events(); } double seconds_per_event() const { return seconds() / events(); } + uint64_t cycles_consumed() const { return m_cpu_cycles_used; } + uint64_t event_mult() const { return m_event_mult; } uint64_t events() const { return m_event_count * m_event_mult; } const std::string& get_name() const { return m_name; } @@ -207,6 +223,7 @@ class Timer uint64_t m_event_count = 0, m_event_mult = 0; uint64_t m_max_time = 0, m_min_time = 0; + uint64_t m_cpu_cycles_start = 0, m_cpu_cycles_used = 0; }; std::string Timer::result_string_bps(const Timer& timer) @@ -223,8 +240,15 @@ std::string Timer::result_string_bps(const Timer& timer) oss << " " << timer.doing(); oss << " " << std::fixed << std::setprecision(3) - << MiB_per_sec << " MiB/sec" - << " (" << MiB_total << " MiB in " << timer.milliseconds() << " ms)\n"; + << MiB_per_sec << " MiB/sec"; + + if(timer.cycles_consumed() != 0) + { + const double cycles_per_byte = static_cast<double>(timer.cycles_consumed()) / timer.events(); + oss << " " << std::fixed << std::setprecision(2) << cycles_per_byte << " cycles/byte"; + } + + oss << " (" << MiB_total << " MiB in " << timer.milliseconds() << " ms)\n"; return oss.str(); } @@ -246,8 +270,16 @@ std::string Timer::result_string_ops(const Timer& timer) oss << static_cast<uint64_t>(events_per_second) << ' ' << timer.doing() << "/sec; " << std::setprecision(2) << std::fixed - << timer.ms_per_event() << " ms/op" - << " (" << timer.events() << " " << (timer.events() == 1 ? "op" : "ops") + << timer.ms_per_event() << " ms/op"; + + if(timer.cycles_consumed() != 0) + { + const double cycles_per_op = static_cast<double>(timer.cycles_consumed()) / timer.events(); + const size_t precision = (cycles_per_op < 10000) ? 2 : 0; + oss << " " << std::fixed << std::setprecision(precision) << cycles_per_op << " cycles/op"; + } + + oss << " (" << timer.events() << " " << (timer.events() == 1 ? "op" : "ops") << " in " << timer.milliseconds() << " ms)\n"; } @@ -357,11 +389,6 @@ class Speed final : public Command algo, provider, msec, buf_size, std::bind(&Speed::bench_hash, this, _1, _2, _3, _4)); } - else if(auto enc = Botan::get_cipher_mode(algo, Botan::ENCRYPTION)) - { - auto dec = Botan::get_cipher_mode(algo, Botan::DECRYPTION); - bench_cipher_mode(*enc, *dec, msec, buf_size); - } else if(Botan::BlockCipher::providers(algo).size() > 0) { bench_providers_of<Botan::BlockCipher>( @@ -374,6 +401,11 @@ class Speed final : public Command algo, provider, msec, buf_size, std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4)); } + else if(auto enc = Botan::get_cipher_mode(algo, Botan::ENCRYPTION)) + { + auto dec = Botan::get_cipher_mode(algo, Botan::DECRYPTION); + bench_cipher_mode(*enc, *dec, msec, buf_size); + } else if(Botan::MessageAuthenticationCode::providers(algo).size() > 0) { bench_providers_of<Botan::MessageAuthenticationCode>( @@ -483,6 +515,12 @@ class Speed final : public Command } #endif } +#if defined(BOTAN_HAS_SIMD_32) + else if(algo == "simd") + { + bench_simd32(msec); + } +#endif else if(algo == "entropy") { bench_entropy_sources(msec); @@ -559,6 +597,9 @@ class Speed final : public Command const Botan::SymmetricKey key(rng(), cipher.maximum_keylength()); cipher.set_key(key); + const Botan::InitializationVector iv(rng(), 12); + cipher.set_iv(iv.begin(), iv.size()); + while(encrypt_timer.under(runtime)) { encrypt_timer.run([&] { cipher.encipher(buffer); }); @@ -648,6 +689,96 @@ class Speed final : public Command output() << Timer::result_string_bps(timer); } +#if defined(BOTAN_HAS_SIMD_32) + void bench_simd32(const std::chrono::milliseconds msec) + { + const size_t SIMD_par = 32; + static_assert(SIMD_par % 4 == 0, "SIMD input is multiple of 4"); + + Botan::SIMD_4x32 simd[SIMD_par]; + + Timer total_time("", "", "", 0); + + Timer load_le_op("SIMD_4x32", "", "load_le", SIMD_par); + Timer load_be_op("SIMD_4x32", "", "load_be", SIMD_par); + Timer add_op("SIMD_4x32", "", "add", SIMD_par); + Timer sub_op("SIMD_4x32", "", "sub", SIMD_par); + Timer xor_op("SIMD_4x32", "", "xor", SIMD_par); + Timer bswap_op("SIMD_4x32", "", "bswap", SIMD_par); + Timer transpose_op("SIMD_4x32", "", "transpose4", SIMD_par/4); + + std::chrono::milliseconds msec_part = msec / 5; + + uint8_t rnd[16 + SIMD_par]; + rng().randomize(rnd, sizeof(rnd)); + + while(total_time.under(msec)) + { + total_time.start(); + + load_le_op.run([&simd,rnd] { + for(size_t i = 0; i != SIMD_par; ++i) + { + // Test that unaligned loads work ok + simd[i].load_le(rnd + i); + } + }); + + load_be_op.run([&simd,rnd] { + for(size_t i = 0; i != SIMD_par; ++i) + { + simd[i].load_be(rnd + i); + } + }); + + add_op.run([&simd] { + for(size_t i = 0; i != SIMD_par; ++i) + { + simd[i] += simd[(i+8) % SIMD_par]; + } + }); + + xor_op.run([&simd] { + for(size_t i = 0; i != SIMD_par; ++i) + { + simd[i] ^= simd[(i+8) % SIMD_par]; + } + }); + + transpose_op.run([&simd] { + for(size_t i = 0; i != SIMD_par; i += 4) + { + Botan::SIMD_4x32::transpose(simd[i], simd[i+1], simd[i+2], simd[i+3]); + } + }); + + sub_op.run([&simd] { + for(size_t i = 0; i != SIMD_par; ++i) + { + simd[i] -= simd[(i+8) % SIMD_par]; + } + }); + + bswap_op.run([&simd] { + for(size_t i = 0; i != SIMD_par; ++i) + { + simd[i] = simd[i].bswap(); + } + }); + + total_time.stop(); + } + + output() << Timer::result_string_ops(add_op); + output() << Timer::result_string_ops(sub_op); + output() << Timer::result_string_ops(xor_op); + output() << Timer::result_string_ops(bswap_op); + output() << Timer::result_string_ops(load_le_op); + output() << Timer::result_string_ops(load_be_op); + output() << Timer::result_string_ops(transpose_op); + } +#endif + void bench_entropy_sources(const std::chrono::milliseconds) { Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources(); |