#c #boost #openssl #crypto
#c #повышение #openssl #крипто
Вопрос:
Я ищу самую быструю реализацию SHA1, поскольку мне приходится вычислять ее миллионы раз. Я попробовал boost::uuids::detail::sha1 и OpenSSL SHA1 и обнаружил, что OpenSSL в 2,5 раза быстрее boost. Я также проверил Crypto , который намного медленнее, чем два других. Вот что я делаю, чтобы проверить их производительность:
OpenSSL SHA1:
#include "openssl/sha.h"
void sha1_ossl (const unsigned char* data) {
unsigned char hash[20];
for (long i=0; i<100000000; i) {
SHA1(data, 64, hash);
if ((unsigned int)hash[0]==0 amp;amp; (unsigned int)hash[1]==0 amp;amp; (unsigned int)hash[2]==0 amp;amp; (unsigned int)hash[3]==0)
break;
}
}
Boost::SHA1:
#include <boost/uuid/detail/sha1.hpp>
void sha1_boost (const unsigned char* data) {
boost::uuids::detail::sha1 sha1;
unsigned hash[5];
for (long i=0; i<100000000; i) {
sha1.process_bytes(data, 64);
sha1.get_digest(hash);
sha1.reset();
if (hash[0]==0) break;
}
}
CryptoPP::SHA1:
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
void sha1_cryptoPP (const unsigned char* data) {
std::string data_s (reinterpret_cast<char const*>(data));
std::string hash_hex;
CryptoPP::SHA1 sha1;
for (long i=0; i<100000000; i) {
CryptoPP::StringSource ss(data_s, true, new CryptoPP::HashFilter(sha1, new CryptoPP::HexEncoder(new CryptoPP::StringSink(hash_hex))));
if (hash_hex.starts_with("00000000")) break;
}
}
Затем я тестирую эти функции со случайными данными:
int main() {
const unsigned char data[65] = "tJQVfvcjGMNIvJfowXBjmSRcKtSjCcyQvaAdakfEJtgSNZHnOHCjkzGFwngiLFPm";
sha1_boost (data);
sha1_ossl (data);
sha1_cryptoPP (data);
}
Результаты производительности
Я скомпилировал все коды с g -O3 -std=c 2a
, чтобы получить следующие результаты. Я обнаружил, что OpenSSL быстрее других реализаций, а Crypto — самый медленный:
Вопросы
- Какая самая быстрая реализация SHA1?
- Как я могу улучшить свою функцию Crypto ?
Приветствуется любая обратная связь для повышения производительности.
Комментарии:
1. Эта ссылка выглядит интересно. Кажется, что OpenSSL настолько хорош, насколько это возможно.
2. @PaulSanders Благодарит Пола. Ссылка очень полезна. Кажется, что OpenSSL — самая быстрая реализация.
3. Некоторые библиотеки защищены от атак по побочным каналам (по времени), а некоторые нет?
4. У вас есть 2 вопроса. Один запрашивает рекомендации для библиотеки, другой лучше подходит для code-review
5. Мои последние эксперименты подтвердили, что openssl был самым быстрым среди нескольких (включая Crypto и некоторые реализации lose на C с одним исходным кодом, которые я забыл, какие именно)
Ответ №1:
Мои последние эксперименты подтвердили, что openssl был самым быстрым среди нескольких (включая Crypto и некоторые реализации lose на C с одним исходным кодом, которые я забыл, какие именно)
Re: части вопроса, связанные с типом проверки кода:
-
«реализация» boost взята из пространства имен detail и на нее не следует полагаться.
-
Криптопроцессор может извлечь выгоду из использования процедурного интерфейса вместо того, чтобы каждый раз динамически составлять конвейер. В частности, вам не следует преобразовывать в строку для проверки первых n байтов дайджеста. Вероятно, это доминирующая часть среды выполнения из-за повторяющихся распределений.
Соблюдение процедурного интерфейса также может позволить вам использовать элемент reset / clear (цитирование из памяти)
if ((unsigned int)hash[0] == 0 amp;amp; (unsigned int)hash[1] == 0 amp;amp;
(unsigned int)hash[2] == 0 amp;amp; (unsigned int)hash[3] == 0)
break;
Должен был быть простой
if (!(hash[0] || hash[1] || hash[2] || hash[3]))
break;
Или даже
if (!std::any_of(hash 0, hash 4, std::identity{}))
break;
Улучшенный тестовый код
Включает в себя некоторые из вышеперечисленных и некоторые другие (в основном в хорошем стиле, избегая ошибок указателя, показывая эффективные итерации и позволяя dump
проверять дайджесты на точность):
В режиме реального времени в Compiler Explorer
#include "openssl/sha.h"
#include <boost/uuid/detail/sha1.hpp>
#include <algorithm>
#include <iostream>
#include <iomanip>
using byte = unsigned char;
#ifndef ONLINE_DEMO
auto constexpr iterations = 100'000'000;
#else
auto constexpr iterations = 10000;
#endif
static void dump(byte const (amp;a)[20]) {
for (unsigned b : a) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << b;
}
std::cout << std::dec << std::endl;
}
static void dump(uint32_t const (amp;a)[5]) {
for (auto b : a) {
std::cout << std::setw(8) << std::setfill('0') << std::hex << b;
}
std::cout << std::dec << std::endl;
}
long sha1_ossl(std::string_view data) {
byte hash[20];
for (long i = 0; i < iterations; i) {
SHA1(reinterpret_cast<byte const*>(data.data()), data.size(), hash);
//dump(hash);
if (!std::any_of(hash 0, hash 4, std::identity{}))
return i;
}
return iterations;
}
long sha1_boost(std::string_view data) {
boost::uuids::detail::sha1 sha1;
uint32_t hash[5];
for (long i = 0; i < iterations; i) {
sha1.process_bytes(reinterpret_cast<byte const*>(data.data()), data.size());
sha1.get_digest(hash);
sha1.reset();
//dump(hash);
if (hash[0] == 0)
return i;
}
return iterations;
}
#ifndef ONLINE_DEMO
#include <cryptopp/hex.h>
#include <cryptopp/sha.h>
long sha1_cryptoPP(std::string_view data) {
byte digest[20];
CryptoPP::SHA1 sha1;
for (long i = 0; i < iterations; i) {
sha1.Restart();
sha1.Update(reinterpret_cast<byte const*>(data.data()), data.size());
sha1.Final(digest);
//dump(digest);
if (!std::any_of(digest 0, digest 4, std::identity{}))
return i;
}
return iterations;
}
#endif
#include <chrono>
using namespace std::chrono_literals;
int main() {
static auto now = std::chrono::high_resolution_clock::now;
constexpr static std::string_view data =
"tJQVfvcjGMNIvJfowXBjmSRcKtSjCcyQvaAdakfEJtgSNZHnOHCjkzGFwngiLFPm";
auto timed = [](auto caption, auto f) {
auto const start = now();
auto n = f(data);
std::cout << caption << ": " << n << " in " << (now()-start)/1.0ms << "msn";
};
timed("sha1_boost", sha1_boost);
timed("sha1_ossl", sha1_ossl);
#ifndef ONLINE_DEMO
timed("sha1_cryptoPP", sha1_cryptoPP);
#endif
}
С принтами:
sha1_boost: 100000000 in 85660.5ms
sha1_ossl: 100000000 in 24652.6ms
sha1_cryptoPP: 100000000 in 34921.3ms
Или онлайн (там нет крипто ):
sha1_boost: 10000 in 8.71938ms
sha1_ossl: 10000 in 2.32025ms
Это значительное улучшение, но победитель тот же.
Комментарии:
1. У меня была небольшая ошибка в
timed
, возможно, вы поняли, что тайминги были кумулятивными : (Исправлено сейчас, чтобы это было менее запутанным!2. Вы утверждаете, что OpenSSL «был самым быстрым среди»… но из проводника компилятора я вижу: sha1_boost: 10000 за 9.2606 мс, sha1_ossl: 10000 за 15.9871 мс. Я пытаюсь выяснить, какая из этих библиотек предлагает лучшую реализацию SHA-256 с точки зрения скорости и использования процессора :/
3. @NorbertBoros скорость зависит от компиляторов, флагов, архитектур и т.д. Итак, да, всегда проводите свое собственное профилирование. Удивительно, что результаты в Compiler Explorer изменились. Я думаю, это показывает, что вам нужно повторять тесты при изменении версий. Вы можете увидеть результаты от 2020/08/20 в моем посте, которые были явно > в 3 раза быстрее для boost.
4. @NorbertBoros Я только что повторил тесты на моем процессоре i7-3770K с частотой 3,50 ГГц и неизменно получал одинаковые тайминги: (100 ‘000’000) sha1_boost: 78.8104 с, sha1_ossl: 21.8883 с, sha1_cryptoPP: 30.4004 с. Это libssl1.1: amd64, boost 1.75, GCC 10, c 17 и libcrypto 6 (= 5.6.4-8), все на Ubuntu 18.04 (я знаю, довольно старая, но не должна иметь большого значения)
5. Да, я думаю, это действительно, я только что провел свой собственный тест на AMD Ryzen, и OpenSSL потрясающий по сравнению с crypto с SHA256. Я предполагаю, что победителем является OpenSSL во что бы то ни стало, потому что он просто кажется лучше оптимизированным.