gpt4 book ai didi

c++ - 如何提高C++中merkle根的计算速度?

转载 作者:行者123 更新时间:2023-12-04 01:03:24 25 4
gpt4 key购买 nike

我正在尝试尽可能优化 merkle 根计算。到目前为止,我用 Python 实现了它,这导致了 this question 和用 C++ 重写它的建议。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>

#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>



std::vector<unsigned char> double_sha256(std::vector<unsigned char> a, std::vector<unsigned char> b)
{
unsigned char inp[64];
int j=0;
for (int i=0; i<32; i++)
{
inp[j] = a[i];
j++;
}
for (int i=0; i<32; i++)
{
inp[j] = b[i];
j++;
}

const EVP_MD *md_algo = EVP_sha256();
unsigned int md_len = EVP_MD_size(md_algo);
std::vector<unsigned char> out( md_len );
EVP_Digest(inp, 64, out.data(), &md_len, md_algo, nullptr);
EVP_Digest(out.data(), md_len, out.data(), &md_len, md_algo, nullptr);
return out;
}

std::vector<std::vector<unsigned char> > calculate_merkle_root(std::vector<std::vector<unsigned char> > inp_list)
{
std::vector<std::vector<unsigned char> > out;
int len = inp_list.size();
if (len == 1)
{
out.push_back(inp_list[0]);
return out;
}
for (int i=0; i<len-1; i+=2)
{
out.push_back(
double_sha256(inp_list[i], inp_list[i+1])
);
}
if (len % 2 == 1)
{
out.push_back(
double_sha256(inp_list[len-1], inp_list[len-1])
);
}
return calculate_merkle_root(out);
}



int main()
{
std::ifstream infile("txids.txt");

std::vector<std::vector<unsigned char> > txids;
std::string line;
int count = 0;
while (std::getline(infile, line))
{
unsigned char* buf = OPENSSL_hexstr2buf(line.c_str(), nullptr);
std::vector<unsigned char> buf2;
for (int i=31; i>=0; i--)
{
buf2.push_back(
buf[i]
);
}
txids.push_back(
buf2
);
count++;
}
infile.close();
std::cout << count << std::endl;

std::vector<std::vector<unsigned char> > merkle_root_hash;
for (int k=0; k<1000; k++)
{
merkle_root_hash = calculate_merkle_root(txids);
}
std::vector<unsigned char> out0 = merkle_root_hash[0];
std::vector<unsigned char> out;
for (int i=31; i>=0; i--)
{
out.push_back(
out0[i]
);
}

static const char alpha[] = "0123456789abcdef";
for (int i=0; i<32; i++)
{
unsigned char c = out[i];
std::cout << alpha[ (c >> 4) & 0xF];
std::cout << alpha[ c & 0xF];
}
std::cout.put('\n');

return 0;
}

但是,与 Python 实现相比,性能更差(~4s):

$ g++ test.cpp -L/usr/local/opt/openssl/lib -I/usr/local/opt/openssl/include -lcrypto
$ time ./a.out
1452
289792577c66cd75f5b1f961e50bd8ce6f36adfc4c087dc1584f573df49bd32e

real 0m9.245s
user 0m9.235s
sys 0m0.008s

完整的实现和输入文件可在此处获得:test.cpptxids.txt

如何提高性能?默认情况下是否启用编译器优化?是否有比可用的 openssl 更快的 sha256 库?

最佳答案

您可以做很多事情来优化代码。

这里是要点列表:

  • 需要启用编译器优化(在 GCC 中使用 -O3);
  • std::array 可以用来代替较慢的动态大小的 std::vector(因为散列的大小是32),为了清晰起见,甚至可以定义一个新的Hash类型;
  • 参数应该通过引用传递(C++默认通过复制传递参数)
  • 可以保留 C++ vector 以预分配内存空间并避免不需要的拷贝;
  • OPENSSL_free 必须调用以释放分配的内存 OPENSSL_hexstr2buf
  • push_back 当大小是编译时已知的常量时应避免;
  • 使用 std::copy 通常比手动复制更快(更干净);
  • std::reverse 通常比手动循环更快(更干净);
  • 散列的大小应该是 32,但是可以使用断言来检查它是否正确;
  • count 不是必需的,因为它是 txids vector 的大小;

这是结果代码:

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <cstring>
#include <array>
#include <algorithm>
#include <cassert>

#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>

using Hash = std::array<unsigned char, 32>;

Hash double_sha256(const Hash& a, const Hash& b)
{
assert(a.size() == 32 && b.size() == 32);

unsigned char inp[64];
std::copy(a.begin(), a.end(), inp);
std::copy(b.begin(), b.end(), inp+32);

const EVP_MD *md_algo = EVP_sha256();
assert(EVP_MD_size(md_algo) == 32);

unsigned int md_len = 32;
Hash out;
EVP_Digest(inp, 64, out.data(), &md_len, md_algo, nullptr);
EVP_Digest(out.data(), md_len, out.data(), &md_len, md_algo, nullptr);
return out;
}

std::vector<Hash> calculate_merkle_root(const std::vector<Hash>& inp_list)
{
std::vector<Hash> out;
int len = inp_list.size();
out.reserve(len/2+2);
if (len == 1)
{
out.push_back(inp_list[0]);
return out;
}
for (int i=0; i<len-1; i+=2)
{
out.push_back(double_sha256(inp_list[i], inp_list[i+1]));
}
if (len % 2 == 1)
{
out.push_back(double_sha256(inp_list[len-1], inp_list[len-1]));
}
return calculate_merkle_root(out);
}

int main()
{
std::ifstream infile("txids.txt");

std::vector<Hash> txids;
std::string line;
while (std::getline(infile, line))
{
unsigned char* buf = OPENSSL_hexstr2buf(line.c_str(), nullptr);
Hash buf2;
std::copy(buf, buf+32, buf2.begin());
std::reverse(buf2.begin(), buf2.end());
txids.push_back(buf2);
OPENSSL_free(buf);
}
infile.close();
std::cout << txids.size() << std::endl;

std::vector<Hash> merkle_root_hash;
for (int k=0; k<1000; k++)
{
merkle_root_hash = calculate_merkle_root(txids);
}
Hash out0 = merkle_root_hash[0];
Hash out = out0;
std::reverse(out.begin(), out.end());

static const char alpha[] = "0123456789abcdef";
for (int i=0; i<32; i++)
{
unsigned char c = out[i];
std::cout << alpha[ (c >> 4) & 0xF];
std::cout << alpha[ c & 0xF];
}
std::cout.put('\n');

return 0;
}

在我的机器上,这段代码比初始版本快 3 倍,比 Python 实现快 2 倍。

此实现 将 >98% 的时间花在 EVP_Digest 中。因此,如果您想要更快的代码,您可以尝试找到一个更快的散列库,尽管 OpenSSL 应该已经相当快了。目前的代码已经成功地在主流 CPU 上每秒连续计算 170 万个哈希值。这很好。或者,您也可以使用 OpenMP 并行化程序(这在我的 6 核机器上大约快 5 倍)。

关于c++ - 如何提高C++中merkle根的计算速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67364134/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com