在分析表时,加密和解密的区别是否意味着我的实现异常缓慢?我是不是做错了什么?
三四件事突然出现在我身上。我有点同意@JamesKPolk - 数字看起来不对。首先,加密库通常以 CTR 模式而不是 CBC 模式为基准。另请参阅SUPERCOP benchmarks。您必须使用每字节周期数 (cpb) 来规范跨机器的测量单位。在没有上下文的情况下说“9 MB/s”毫无意义。
其次,我们需要知道机器及其 CPU 频率。看起来您正在以 9 MB/s 的速度推送数据进行加密,以 6.5 MB/s 的速度进行解密。现代 iCore 机器,如Core-i5 running at 2.7 GHz,将以 2.5 或 3.0 cpb 左右的速度推送 CBC 模式数据,大约为 980 MB/s 或 1 GB/s。甚至我的旧Core2 Duo running at 2.0 GHz 移动数据的速度也比您显示的要快。 Core 2 以 14.5 cpb 或 130 MB/s 的速度移动数据。
第三,废弃这段代码。有很大的改进空间,因此在这种情况下不值得批评;推荐的代码如下。值得一提的是,您正在创建很多对象,例如 ArraySource 和 StreamTransformationFilter。过滤器添加了填充,这会扰乱 AES 加密和解密基准并扭曲结果。只需要一个加密对象,然后调用ProcessBlock或ProcessString即可。
CryptoPP::AES::Decryption aesDecryption(aesKey, ENCRYPTION_KEY_SIZE_AES);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, aesIv);
...
第四,Crypto++ wiki 有一个Benchmarks article,代码如下。它是一个新部分,当您提出问题时不可用。以下是运行测试的方法。
AutoSeededRandomPool prng;
SecByteBlock key(16);
prng.GenerateBlock(key, key.size());
CTR_Mode<AES>::Encryption cipher;
cipher.SetKeyWithIV(key, key.size(), key);
const int BUF_SIZE = RoundUpToMultipleOf(2048U,
dynamic_cast<StreamTransformation&>(cipher).OptimalBlockSize());
AlignedSecByteBlock buf(BUF_SIZE);
prng.GenerateBlock(buf, buf.size());
const double runTimeInSeconds = 3.0;
const double cpuFreq = 2.7 * 1000 * 1000 * 1000;
double elapsedTimeInSeconds;
unsigned long i=0, blocks=1;
ThreadUserTimer timer;
timer.StartTimer();
do
{
blocks *= 2;
for (; i<blocks; i++)
cipher.ProcessString(buf, BUF_SIZE);
elapsedTimeInSeconds = timer.ElapsedTimeAsDouble();
}
while (elapsedTimeInSeconds < runTimeInSeconds);
const double bytes = static_cast<double>(BUF_SIZE) * blocks;
const double ghz = cpuFreq / 1000 / 1000 / 1000;
const double mbs = bytes / 1024 / 1024 / elapsedTimeInSeconds;
const double cpb = elapsedTimeInSeconds * cpuFreq / bytes;
std::cout << cipher.AlgorithmName() << " benchmarks..." << std::endl;
std::cout << " " << ghz << " GHz cpu frequency" << std::endl;
std::cout << " " << cpb << " cycles per byte (cpb)" << std::endl;
std::cout << " " << mbs << " MiB per second (MiB)" << std::endl;
在 Core-i5 6400 上以 2.7 GHz 运行代码会导致:
$ ./bench.exe
AES/CTR benchmarks...
2.7 GHz cpu frequency
0.58228 cycles per byte (cpb)
4422.13 MiB per second (MiB)
第五,当我修改上面显示的基准程序以操作64字节块时:
const int BUF_SIZE = 64;
unsigned int blocks = 0;
...
do
{
blocks++;
cipher.ProcessString(buf, BUF_SIZE);
elapsedTimeInSeconds = timer.ElapsedTimeAsDouble();
}
while (elapsedTimeInSeconds < runTimeInSeconds);
对于 64 字节块,Core-i5 6400 在 2.7 GHz 下的速度为 3.4 cpb 或 760 MB/s。该库因缓冲区较小而受到打击,但大多数(所有?)库都会这样做。
$ ./bench.exe
AES/CTR benchmarks...
2.7 GHz cpu frequency
3.39823 cycles per byte (cpb)
757.723 MiB per second (MiB)
第六,您需要让处理器退出省电模式或低能耗状态,以获得最佳/最一致的结果。该库使用governor.sh 在 Linux 上执行此操作。它位于TestScript/ 目录中。
第七,当我切换到CTR模式解密时:
CTR_Mode<AES>::Decryption cipher;
cipher.SetKeyWithIV(key, key.size(), key);
然后我看到批量解密的速率大致相同:
$ ./bench.exe
AES/CTR benchmarks...
2.7 GHz cpu frequency
0.579923 cycles per byte (cpb)
4440.11 MiB per second (MiB)
第八,这里是一组不同机器上的基准数据。它应该在您调整测试时提供一个粗略的目标。
运行频率为 980 MHz 的 Beaglebone 开发板移动数据的速率是您报告的两倍。 Beaglebone 实现了无聊的 40 cpb 和 20 MB/s,因为它是标准的 C/C++;并且没有针对 A-32 进行优化。
-
Skylake Core-i5 @2.7 GHz
-
Core2 Duo @ 2.0 GHz
-
LeMaker HiKey Kirik SoC Aarch64 @1.2 GHz
-
AMD Opteron Aarch64 @2.0 GHz
-
BananaPi Cortex-A7 开发板@860MHz
-
IBM Power8 服务器@4.1 GHz
我的收获是:
- CTR 模式批量加密和解密在现代机器上大致相同
- CTR 模式密钥设置不一样,在现代机器上解密需要更长的时间
- 小块比大块更昂贵
这是我希望看到的一切。
我认为您的下一步是使用 Crypto++ wiki 上的the sample program 收集一些数据,然后评估结果。