Jak*_* S. -1 python cryptography rust
在阅读了一篇关于“Memorable Unique Identifiers”的文章并看到作者提到他们想尝试将示例Python代码重写为C以获得更好的性能后,我尝试将其重写为Rust,我对Rust版本的大幅提升感到非常惊讶比 Python 版本慢。
Python 3.10.9在配备和 的2021 M1 Pro MacBook Pro 上进行了测试nightly-aarch64-apple-darwin, rustc 1.69.0-nightly (07c993eba 2023-02-23)。
文章中的 Python 版本(对原文稍加编辑)
import json
import hashlib
import os
TARGET_DIFF = 8
prefixes = set()
with open("animals.json") as f:
f = json.load(f)
for key in f.keys():
if len(key) == TARGET_DIFF:
prefixes.add(key)
counter = 0
while counter < 10:
buf = os.urandom(16).hex()
h = hashlib.sha256(buf.encode("utf-8")).digest().hex()
if h[:TARGET_DIFF] in prefixes:
print(buf, h)
counter += 1
Run Code Online (Sandbox Code Playgroud)
这可以在大约 7 秒内找到 10 个哈希值。
> time python3 miner.py
python3 miner.py 7.60s user 3.00s system 99% cpu 10.626 total
Run Code Online (Sandbox Code Playgroud)
我尝试将其重写为 Rust:
> time python3 miner.py
python3 miner.py 7.60s user 3.00s system 99% cpu 10.626 total
Run Code Online (Sandbox Code Playgroud)
编译后cargo build --release,它在 44 秒内找到了 10 个哈希值......
> ./target/release/uid 0
./target/release/muid 0 44.79s user 0.13s system 99% cpu 44.986 total
Run Code Online (Sandbox Code Playgroud)
我的猜测是要么是用户错误,因为我是 Rust 新手,要么是 RNG 由于某种原因在 Rust 中速度较慢。有人可以解释一下为什么 Rust 版本慢得多吗?
问题是你使用的代码比 python 版本慢得多。
fill_with_random速度慢得可怕。它逐字节填充数组。rng.fill_bytes()直接使用。rand_xoshiro?我不确定它的性能特征是什么来生成您需要的随机数据。不管怎样,它肯定没有针对生成u8数字进行优化。我个人会先尝试默认的thread_rng()。.clone(),这又会导致内存分配。最好根本不转换;摘要是原始字节,因此将所有内容保留为原始字节。sha2::Sha256使用最广泛,并按预期提供字节数组摘要。clap您的命令行。这与您提出的问题无关,它只是比编写自己的解析器更好、更容易:)serde_json.Vec进行contains()操作 - 它非常慢,就像在O(n). 相反HashSet,它可以在 中执行相同的任务O(log n)。该contains()操作是为什么HashSet存在的全部理由。完成所有这些修复后,这是一个工作版本:
use std::{
collections::{HashMap, HashSet},
fs::File,
io::BufReader,
path::Path,
};
use hex::FromHex;
use sha2::{Digest, Sha256};
use rand::{thread_rng, RngCore};
const TARGET_DIFF: usize = 8;
const TARGET_DIFF_BYTES: usize = TARGET_DIFF / 2;
fn lines_from_file(filename: impl AsRef<Path>) -> HashSet<[u8; TARGET_DIFF_BYTES]> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
let data: HashMap<String, [u8; 2]> =
serde_json::from_reader(buf).expect("Unable to parse input data!");
data.keys()
.filter_map(|s| <[u8; TARGET_DIFF_BYTES]>::from_hex(s).ok())
.collect()
}
fn main() {
let lines = lines_from_file("animals.json");
let mut rng = thread_rng();
println!(
"{0} {1: >31} {2: >61}",
"Private Key", "Public Key", "Keyword"
);
println!("-----------------------------------------------------------------------------------------------------------");
let mut counter = 0;
while counter < 10 {
let mut rand: [u8; 16] = [0; 16];
rng.fill_bytes(&mut rand);
let data_digest = Sha256::digest(&rand);
let head = &data_digest[..TARGET_DIFF_BYTES];
if lines.contains(head) {
println!(
"{} {} {}",
hex::encode(rand),
hex::encode(data_digest),
hex::encode(head)
);
counter += 1;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在我的 PC 上,Python 代码大约需要 15 秒,Rust 代码大约需要 4 秒。
我确实尝试过Xoshiro256PlusPlus,但似乎没有多大区别。如果说有什么不同的话,那就是感觉有点慢了。
现在是您使用 Rust 的实际原因。你可以做进一步的优化,这在 Python 中是不可能的。在这种情况下,代码将从多线程中受益匪浅——由于全局解释器锁(GIL),这个概念在Python中根本不存在。
这是代码的简单多线程版本:
use std::{
collections::{HashMap, HashSet},
fs::File,
io::BufReader,
path::Path,
sync::atomic::{AtomicU8, Ordering},
time::Instant,
};
use hex::FromHex;
use sha2::{Digest, Sha256};
use rand::{thread_rng, RngCore};
const TARGET_DIFF: usize = 8;
const TARGET_DIFF_BYTES: usize = TARGET_DIFF / 2;
fn lines_from_file(filename: impl AsRef<Path>) -> HashSet<[u8; TARGET_DIFF_BYTES]> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
let data: HashMap<String, [u8; 2]> =
serde_json::from_reader(buf).expect("Unable to parse input data!");
data.keys()
.filter_map(|s| <[u8; TARGET_DIFF_BYTES]>::from_hex(s).ok())
.collect()
}
fn main() {
let t0 = Instant::now();
let lines = lines_from_file("animals.json");
let t1 = Instant::now();
println!(
"{0} {1: >31} {2: >61}",
"Private Key", "Public Key", "Keyword"
);
println!("-----------------------------------------------------------------------------------------------------------");
let counter: AtomicU8 = AtomicU8::new(0);
std::thread::scope(|scope| {
for thread_id in 0..num_cpus::get() {
let lines = &lines;
let counter = &counter;
scope.spawn(move || {
let mut rng = thread_rng();
while counter.load(Ordering::Relaxed) < 10 {
let mut rand: [u8; 16] = [0; 16];
rng.fill_bytes(&mut rand);
let data_digest = Sha256::digest(rand);
let head = &data_digest[..TARGET_DIFF_BYTES];
if lines.contains(head) {
println!(
"{} {} {} (thread {})",
hex::encode(rand),
hex::encode(data_digest),
hex::encode(head),
thread_id
);
counter.fetch_add(1, Ordering::Relaxed);
}
}
});
}
});
let t2 = Instant::now();
println!();
println!("Json parser: {} ms", (t1 - t0).as_millis());
println!("Computation: {} ms", (t2 - t1).as_millis());
}
Run Code Online (Sandbox Code Playgroud)
$ time ./target/release/rust_playground
Private Key Public Key Keyword
-----------------------------------------------------------------------------------------------------------
4899c6115970e257f5487a5d7a57df8b a1beba55ba89c9523d6e33ae0896efb95873a04c3a39c143a89e26f69ca00fc0 a1beba55 (thread 1)
ac1e8cc6bf8bb4d03a2feb7afed9d48c 53e76f08669014031bcf2e85ff263f220f883caafd039c107601ae7b4828b3b8 53e76f08 (thread 2)
aa5d4813fe281df2ad982bd630f98f10 3e1ea91e84d3ff3af0c59c262711d084de9a5c1260053aae2ba2b361aa8fd541 3e1ea91e (thread 6)
bf41fd645743740f32d02fbc9ddaf98e 1e9b00b4117ecd90d6e36e2dc9a36b12620fe20449cc19b7b8f4de6d781557de 1e9b00b4 (thread 5)
7a56d3284dcbdfb070744fef2186071d 3a55aee106225fe476c99b83bebaed50c87702ef00ad9a8fc56fa9d114ef661e 3a55aee1 (thread 7)
f0074e27fd48b60d739b667b708a1d62 be1a70addbf096285d1734891ac6d3b56f06a057683d77b586816ef10599492b be1a70ad (thread 3)
3b9b98957272e1c079fdd0265d96b834 fe3e90a72fc8f2008624a80dc840a2964405a466d2d6301cb650777c00de46a0 fe3e90a7 (thread 0)
d0db8ceb534fe07d9206c82a920464b2 a71e301e411faa78f87f264efb20a5b01b75125fafddd0728fd19340501faaab a71e301e (thread 5)
92f2a0d5af8ce2b963cb2e72760de46a 10b3005ebb708ba114850dd99fdc8286101235664121d90e082963314bd23289 10b3005e (thread 5)
e7c227036024053ae04a55a819b30e4a 7ee57b0af545bfc545277b6f7854ac740b6110b5d1a6eeb1dae002d8c6c10c6b 7ee57b0a (thread 7)
Json parser: 26 ms
Computation: 926 ms
real 0m0.960s
user 0m7.338s
sys 0m0.062s
Run Code Online (Sandbox Code Playgroud)
与 Python 中的 15 秒相比。请注意,real和 之间user存在巨大差异,这在 Python 中通常不会发生。这意味着如果将各个核心的所有时间相加,实际时间将大大低于有效时间。这是一件好事——这意味着我们的多线程代码可以工作。
为了进行比较,这是我的 python 代码的结果:
$ time python3 main.py
0b9a4dc5090db954b02bfbb87d571a35 3e564d09959cff5b074726e7535a82a6030ba0295922f71bb1891e01b4516c75
1279f0153e4e847d5908142a1cacf58e a7305b0a806c9f62bb5112efebee9f1d6b8283eb37847a0f134b83aa94aeda00
3e94485a9d09cef92b98e4f4ef9da60d 57e3af14e914bb23c248e8a75ca24abf2b09557fae744a8505ac7a61f4cc3107
9cd343269aa18dcb088b5ceb37d7a42c 30554d09cf8879eb1bf90b1de1040449ada311976c782008718c9d40e885506b
8ab684a1be71c469eba7df5ff2aae11c 5c007f0811ffc2aa5ac00beb9f5ae855151775fa5618483b62814d9344e53924
60880dc454f7c7c06266bbcbde8b6e82 d03e301e1c4eed6ac15768c96221bbec49a0c2efa39f2a161dd0ad89955af35b
d239f0435e2b3f5b8d9416c2f20db09f b01dba55a8241eb97f226158958b39a87f847f0e94ae8b2bf9e7a1fe91fc7ce9
039215379ead9258dd9529dc7ac9f4fd 5a6690a7d25accced56a61919119ceb02a2366418de3cec71cbca41025ed9699
4e6aff88a6145e9a8dd2ab05e2ca1d81 9ab1ef14e7f2d5a0d1758c6a6e905f1874c760408ddd6d091933941367d2a071
a0c2275229200da2357d458447d87d3c 605e1ca7e580712e315961f37c4ff7779873d5212dcd1680340d6430bd93c6b3
real 0m21.578s
user 0m17.667s
sys 0m3.893s
Run Code Online (Sandbox Code Playgroud)
这里有一些更小的优化,尽管我不确定它们真正有多大(如果有)影响:
SHA256对象,并且不要每次都为输出分配缓冲区对于生产或提交,我将使用上面的代码。我只是想将此作为其他可能更改的内容的参考。
$ time ./target/release/rust_playground
Private Key Public Key Keyword
-----------------------------------------------------------------------------------------------------------
4899c6115970e257f5487a5d7a57df8b a1beba55ba89c9523d6e33ae0896efb95873a04c3a39c143a89e26f69ca00fc0 a1beba55 (thread 1)
ac1e8cc6bf8bb4d03a2feb7afed9d48c 53e76f08669014031bcf2e85ff263f220f883caafd039c107601ae7b4828b3b8 53e76f08 (thread 2)
aa5d4813fe281df2ad982bd630f98f10 3e1ea91e84d3ff3af0c59c262711d084de9a5c1260053aae2ba2b361aa8fd541 3e1ea91e (thread 6)
bf41fd645743740f32d02fbc9ddaf98e 1e9b00b4117ecd90d6e36e2dc9a36b12620fe20449cc19b7b8f4de6d781557de 1e9b00b4 (thread 5)
7a56d3284dcbdfb070744fef2186071d 3a55aee106225fe476c99b83bebaed50c87702ef00ad9a8fc56fa9d114ef661e 3a55aee1 (thread 7)
f0074e27fd48b60d739b667b708a1d62 be1a70addbf096285d1734891ac6d3b56f06a057683d77b586816ef10599492b be1a70ad (thread 3)
3b9b98957272e1c079fdd0265d96b834 fe3e90a72fc8f2008624a80dc840a2964405a466d2d6301cb650777c00de46a0 fe3e90a7 (thread 0)
d0db8ceb534fe07d9206c82a920464b2 a71e301e411faa78f87f264efb20a5b01b75125fafddd0728fd19340501faaab a71e301e (thread 5)
92f2a0d5af8ce2b963cb2e72760de46a 10b3005ebb708ba114850dd99fdc8286101235664121d90e082963314bd23289 10b3005e (thread 5)
e7c227036024053ae04a55a819b30e4a 7ee57b0af545bfc545277b6f7854ac740b6110b5d1a6eeb1dae002d8c6c10c6b 7ee57b0a (thread 7)
Json parser: 26 ms
Computation: 926 ms
real 0m0.960s
user 0m7.338s
sys 0m0.062s
Run Code Online (Sandbox Code Playgroud)