aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-02-02 22:44:39 -0500
committerJack Lloyd <[email protected]>2017-02-02 22:44:39 -0500
commit2a7127fea6e7d987432de907165eaadf30cb3cbc (patch)
treea7eb44566f8aa9dd6c83b5f6aabd819e0d684f16
parent23a96ca126ebfddb2259386f12a00a8e783226ed (diff)
Report cycles/byte in speed tests
Add SIMD perf test
-rw-r--r--src/cli/speed.cpp169
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();