Почему вычисление sha-256 в python быстрее чем в c#?
Я играюсь с двумя языками и решил сравнить скорость вычисления. Написал маленькие программы практически идентичные которые ищут хеш sha-256 с нулями впереди. И почему-то python выходит быстрее и находит длинною 5 нулей за 2.8 секунды, а c# за 6.8? Строки из которых вычисляться хеш и соответственно полученные хеши одинаковы. Я задаю этот вопрос потому что не вижу логики, почему интерпретируемый язык в данных вычислениях в 2,5 раза быстрее и мне интересна причина. Скрипты запускаю через .bat файл один за другим - почти одновременно.
Вот коды:
C#
using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography;
namespace HelloWorld
{
class Hello {
static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();
Console.WriteLine("Hello!");
int len = 0;
if (args.Length > 0 && args[0] != null)
{
len = Convert.ToInt32(args[0]);
}
else
{
Console.Write("Mask length: ");
len = Convert.ToInt32(Console.ReadLine());
}
string mask = "";
for (int i = 0; i < len; i++)
{
mask += "0";
}
Console.WriteLine("Mask: " + mask);
string str = "Num #";
string hash = "";
Console.WriteLine("Search...");
timer.Start();
for (int num = 0; true; num++)
{
hash = sha256(str + num);
if (hash.IndexOf(mask) == 0)
{
str = str + num;
break;
}
}
Console.WriteLine("Find!");
Console.WriteLine("Data: " + str);
Console.WriteLine("Hash: " + hash);
Console.WriteLine("Done: " + timer.Elapsed);
Console.ReadKey();
}
static string sha256(string randomString)
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
}
}
Python
import sys
import time
import hashlib
def time_convert(sec):
mins = sec // 60
sec = sec % 60
hours = mins // 60
mins = mins % 60
return "{0}:{1}:{2}".format(int(hours),int(mins),sec)
print("Hello!");
if (len(sys.argv) > 1):
len = int(sys.argv[1]);
else:
len = int(input("Mask len: "));
mask = ""
for i in range(len):
mask += "0"
print("Mask: ", mask)
word = "Num #"
hash = ""
print("Search...")
start_time = time.time()
num = 0
while(True):
hash = hashlib.sha256((word + str(num)).encode()).hexdigest()
if hash.find(mask) == 0:
word = word + str(num)
break
num += 1
end_time = time.time()
time_lapsed = end_time - start_time
time_lapsed = time_convert(time_lapsed)
print("Find!");
print("Data: ", word);
print("Hash: ", hash);
print("Done: ", time_lapsed);
input()
Ответы (1 шт):
Не убегая глубоко в оптимизации. Запускаю ваш код в дебаг сборке.
Hello!
Mask length: 5
Mask: 00000
Search...
Find!
Data: Num #646714
Hash: 00000b4eecf7c2687378eb776f9d7e02f4ee4d46883ec750fdd2e238702ee386
Done: 00:00:09.9405143
Немного оптимизирую.
static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();
Console.WriteLine("Hello!");
int len;
if (args.Length > 0 && args[0] != null)
{
len = int.Parse(args[0]);
}
else
{
Console.Write("Mask length: ");
len = int.Parse(Console.ReadLine());
}
string mask = new string('0', len);
Console.WriteLine("Mask: " + mask);
string str = "Num #";
string hash;
Console.WriteLine("Search...");
timer.Start();
for (int num = 0; true; num++)
{
hash = sha256(str + num);
if (hash.StartsWith(mask, StringComparison.Ordinal))
{
str += num;
break;
}
}
timer.Stop();
Console.WriteLine("Find!");
Console.WriteLine("Data: " + str);
Console.WriteLine("Hash: " + hash.ToLower());
Console.WriteLine("Done: " + timer.Elapsed);
Console.ReadKey();
}
static SHA256Managed crypt = new SHA256Managed();
static string sha256(string randomString)
{
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
return Convert.ToHexString(crypto);
}
Запускаю дебаг сборку.
Hello!
Mask length: 5
Mask: 00000
Search...
Find!
Data: Num #646714
Hash: 00000b4eecf7c2687378eb776f9d7e02f4ee4d46883ec750fdd2e238702ee386
Done: 00:00:00.8002694
Запускаю релиз сборку.
Hello!
Mask length: 5
Mask: 00000
Search...
Find!
Data: Num #646714
Hash: 00000b4eecf7c2687378eb776f9d7e02f4ee4d46883ec750fdd2e238702ee386
Done: 00:00:00.5345979
То есть грубо говоря в 10 раз быстрее стало. И это еще не предел оптимизации.
Не устану говорить: это не C# медленный, а код, который на нем так написан.
Узкое место было в этой строчке hash.IndexOf(mask) == 0. Сравнение строк выполняется не побайтово, а с учетом культуры, активной в данный момент. Чтобы сравнение было быстрым и побайтовым, надо так hash.IndexOf(mask, StringComparison.Ordinal) == 0. Ну или как я выше в коде написал.
Вынос создания класса хэша из цикла хоть и ускоряет процесс, но менее значительно. Convert.ToHexString тоже чисто для сокращения кода.
По-приколу 6 нулей поискал.
Hello!
Mask length: 6
Mask: 000000
Search...
Find!
Data: Num #31184147
Hash: 000000de3652ae021f32fc4cb4b6fda5807ed3b0fbea4641841a8078b5e4f668
Done: 00:00:24.4929515
Немного поигрался с черной магией, то есть спанами, уложился с 6 нулями в 10 секунд, но показывать не буду, потому что для нечетного количества нулей работать не будет, точнее надо допиливать, а это не соответствует условию задачи. То есть если точность маски нужна 8 бит, а не 4, то можно вообще очень быстро сделать. А знать бы вообще для чего это (крипта наверное) - еще быстрее. Ах да, забыл, еще же многопоточку можно прикрутить. Для моего проца - это x8 к скорости или около того.