C ++ openssl SHA256 работает медленнее, чем реализация JDK SHA256 - PullRequest
0 голосов
/ 29 октября 2019

Я работаю над проектом, и, как часть его, мне нужно примерно смоделировать вычисление биткойн-доказательства работы. Это включает в себя итеративное вычисление SHA256 дважды по конкатенации фиксированной строки «BlockHash» и 32-битного одноразового числа типа int, которое увеличивается на каждую итерацию. Если вычисленный хэш меньше строки «TargetHash», мы прерываем цикл и выводим значение nonce.

Я пытаюсь сравнить две последовательные реализации, одна из которых написана с использованием C ++ с использованием реализации SHA256 OpenSSL, а другаяв Java, используя внутреннюю реализацию SHA256 JDK. Я ожидал, что реализация OpenSSL будет намного быстрее, чем JDK, но происходит обратное.

Вот мой код Java:

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


public class SHA256 {
    /**
     * convert byte[] to hex string
     *
     * @param hash
     * @return hex string
     */
    private static String bytesToHex(byte[] hash) {
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < hash.length; i++) {
            String hex = Integer.toHexString(0xff & hash[i]);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * get a sha256 of the input string
     *
     * @param inputString
     * @return resulting hash in hex string
     */
    public static String SHA256(String inputString) {
        try {
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
            return bytesToHex(sha256.digest(inputString.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException ex) {
            System.err.println(ex.toString());
            return null;
        }
    }

    public static void main(String[] args){
        String blockHash = SHA256("Some random string to generate a block hash.");
        System.out.println("blockHash: " + blockHash);
        String targetHash = "000000938023b712892a41e8438e3ff2242a68747105de0395826f60b38d88dc";
        String tmp_hash="undefined";
        int nonce = 0;
        for(nonce=Integer.MIN_VALUE; nonce<=Integer.MAX_VALUE; nonce++) {
            tmp_hash = SHA256(SHA256(blockHash+String.valueOf(nonce)));
            if(targetHash.compareTo(tmp_hash)>0)
                break;
        }
        System.out.println("Resulting Hash: " + tmp_hash);
        System.out.println("Nonce:" + nonce);
    }
}

А это моя реализация на C ++:

#include <iostream>
#include <climits>
#include <cstring>
#include <sstream>
#include <string>
#include <iomanip>
#include "format.h"

using namespace std;

#include <openssl/sha.h>

string sha256(const string str)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, str.c_str(), str.size());
    SHA256_Final(hash, &sha256);
    stringstream ss;
    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        ss << hex << setw(2) << setfill('0') << (int)hash[i];
    }
    return ss.str();
}


int main(int argc, char *argv[])
{   
    string input = "Some random string to generate a block hash.";
    string blockHash = sha256(input);
    cout << "blockHash: " << blockHash << endl;
    string targetHash = "000000938023b712892a41e8438e3ff2242a68747105de0395826f60b38d88dc";
    string tmp_hash="undefined";
    int nonce = 0;

    for(nonce = INT_MIN; nonce <= INT_MAX; nonce++){
        tmp_hash = sha256(sha256(fmt::format("{}{}", blockHash, nonce)));
        if(strcmp(tmp_hash.c_str(), targetHash.c_str()) < 0)
            break;
    }

    cout<<"Resulting Hash: "<<tmp_hash<<endl;
    cout<<"Nonce: "<<nonce<<endl;

    return 0;
}

Выводы, использующие утилиту linux 'time' для измерения времени выполнения:

javac SHA256.java
time java SHA256
blockHash: 596143a6a70a23c86e4b218afeb05d151ed45a39e96368e213d17e0a491d894a
Resulting Hash: 0000008ce61c628ffb00b6668687504fd5d44da0a57adb40d6ff59f8e4af0a4a
Nonce:-2135751361

real    0m22.258s
user    0m22.977s
sys 0m0.097s

g++ -O2 -DFMT_HEADER_ONLY main.cpp -lcrypto -lssl
time ./a.out
blockHash: 596143a6a70a23c86e4b218afeb05d151ed45a39e96368e213d17e0a491d894a
Resulting Hash: 0000008ce61c628ffb00b6668687504fd5d44da0a57adb40d6ff59f8e4af0a4a
Nonce: -2135751361

real    0m35.703s
user    0m35.693s
sys 0m0.005s

Это просто для простого TargetHash, для более сложных различие еще больше. Я почти уверен, что реализация openssl sha256 не является узким местом, и что-то еще есть, но, будучи новичком в C ++, я не уверен, что именно. Ранее я использовал to_string (nonce) и s1.compare (s2), которые я заменил на fmt :: format и strcmp, потому что они быстрее, но все же могли получить только несколько секунд. Любые идеи будут по достоинству оценены.

1 Ответ

1 голос
/ 29 октября 2019

Узким местом для вашего кода C ++ является ваша пользовательская функция bytes_to_string. Вызов функций stringstream в цикле просто сказывается на производительности.

Возможно, вы захотите взглянуть на этот ответ на другой вопрос.


замените функции stringstream наследующий фрагмент кода. Это быстрее, потому что он напрямую манипулирует строковой памятью.

static const char characters[] = "0123456789ABCDEF";
std::string result (SHA256_DIGEST_LENGTH * 2, ' ');
for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
    result[2*i] = characters[(unsigned int) hash[i] >> 4];
    result[2*i+1] = characters[(unsigned int) hash[i] & 0x0F];
}
return result;
...