Rust代码性能远劣于Go的原因排查求助
Rust代码性能远低于Go版本的问题求助
我知道这不是这里的常规问题,但还是希望有人能帮我分析这段Rust代码性能极差的原因——所用词典约有1400个词,我的Go版本代码仅需约5分钟运行完成,而Rust版本却耗时约100分钟。我最初在dictionary_attack函数中有不少.clone()调用,已将其移除;还将部分.unwrap()替换为?运算符和.expect(),原以为这是性能瓶颈,但问题仍未解决。以下是完整的Rust代码:
use std::collections::HashMap; use std::collections::HashSet; use std::fs; use std::fs::File; use std::io::{BufRead, BufReader}; #[allow(dead_code)] struct Key { a: [char; 25], b: [char; 25], c: [char; 25], d: [char; 25], } struct Coords { row: i32, column: i32, } fn letters() -> [char; 25] { let l: [char; 25] = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ]; l } fn format_text(original: String) -> String { let mut result: String = "".to_string(); for i in 0..original.len() { if original.as_bytes()[i] >= 65 && original.as_bytes()[i] <= 90 { result.push(original.as_bytes()[i] as char) } } result } fn scan_tetragrams() -> HashMap<String, i32> { let mut tg: HashMap<String, i32> = HashMap::new(); let file = File::open("../tetragrams.txt").expect("Failed to load tetragram file"); let reader = BufReader::new(&file); for line in reader.lines() { let ln: &str = &line.unwrap_or(String::from("Invalid line")); let fields: Vec<&str> = ln.split(", ").collect(); let n: Result<i32, _> = fields[1].parse(); let num: i32 = match n { Ok(n) => n, Err(_) => panic!("Failed to convert tetragram frequence to i32"), }; tg.insert(fields[0].to_string(), num); } tg } fn gen_reference(n: usize) -> String { let r: Vec<u8> = format_text(read("../referenceText.txt")) .as_bytes() .to_vec(); let mut ref_text: String = String::from(""); for i in 0..n { ref_text = format!("{}{}", ref_text, String::from(r[i] as char)); } ref_text } fn scan_dict() -> Vec<String> { let words: Vec<String> = read("../challenge_word_list.txt") .split(" ") .collect::<Vec<&str>>() .iter() .map(|&s| s.into()) .collect(); //this was painful lmao words[..words.len() - 1].to_vec() //last element is a newline character otherwise } fn read(path: &str) -> String { let f: Result<String, std::io::Error> = fs::read_to_string(path); match f { Ok(s) => s, Err(_) => panic!("Couldn't find file"), } } fn score(text: &String, tetragrams: &HashMap<String, i32>) -> i32 { let bytes = text.as_bytes(); let mut total: i32 = 0; for i in 0..text.len() - 3 { let v: Vec<u8> = (bytes[i..i + 4]).to_vec(); let s: Result<String, _> = String::from_utf8(v); let slice = match s { Ok(s) => s, Err(_) => panic!("Failed to parse window in text"), }; if tetragrams.contains_key(&slice) { total += tetragrams[&slice]; } } total } fn fill_grid(keyword: &mut String) -> [char; 25] { let mut seen: HashSet<char> = HashSet::new(); keyword.retain(|c| { let s = seen.contains(&c); seen.insert(c); !s }); //remove duplicate letters from keyword let last_idx = letters() .iter() .position(|&x| x == keyword.chars().last().unwrap()) .unwrap(); //continues filling up grid with next letter after last letter in keyword let mut grid: Vec<char> = keyword .as_bytes() .to_vec() .iter() .map(|x| *x as char) .collect(); //also painful for i in last_idx..last_idx + 25 { let idx = i % 25; if !seen.contains(&letters()[idx]) { seen.insert(letters()[idx]); grid.push(letters()[idx]); } } let t: Result<[char; 25], _> = grid.try_into(); match t { Ok(arr) => arr, Err(_) => panic!("Failed to fill grid"), } } fn coordinates(grid_index: i32) -> Coords { let row: i32 = grid_index / 5; let column: i32 = grid_index - row * 5; Coords { row, column } } fn grid_index(row: i32, column: i32) -> i32 { row * 5 + column } fn decipher(text: &String, k: Key) -> Option<String> { let mut deciphered: String = String::from(""); let bytes = text.as_bytes(); for i in (0..text.len() - 1).step_by(2) { let v: Vec<u8> = (bytes[i..i + 2]).to_vec(); let i1: usize = k.b.iter().position(|&x| x == v[0] as char)?; let i2: usize = k.c.iter().position(|&x| x == v[1] as char)?; let coords1: Coords = coordinates(i1.try_into().ok()?); let coords2: Coords = coordinates(i2.try_into().ok()?); let d_i1: usize = grid_index(coords1.row, coords2.column) .try_into() .expect("Failed to get grid index 1"); //coords of deciphered letters let d_i2: usize = grid_index(coords2.row, coords1.column) .try_into() .expect("Failed to get grid index 2"); let letter1: String = String::from(k.a[d_i1]); let letter2: String = String::from(k.d[d_i2]); deciphered = format!("{}{}{}", deciphered, letter1, letter2); } Some(deciphered) } fn dictionary_attack(ciphertext: &String) { let dict = scan_dict(); let tetragrams = scan_tetragrams(); let ref_score = score(&gen_reference(ciphertext.clone().len()), &tetragrams); let mut count: i32 = 0; for word in &dict { println!("{}", count); count += 1; if word.contains("J") || word.len() == 1 { continue; } for word2 in &dict { if word2.contains("J") || word2.len() == 1 { continue; } let k: Key = Key { a: letters(), b: fill_grid(&mut word.to_string()), c: fill_grid(&mut word2.to_string()), d: letters(), }; let p = decipher(&ciphertext, k).unwrap(); if score(&p, &tetragrams) * 10 >= ref_score * 8 { println!("{}", p) } } } } fn main() { let original = read("src/ciphertext.txt"); let ciphertext: String = format_text(original); dictionary_attack(&ciphertext) }
核心性能瓶颈分析
1. 字符串拼接的低效操作
gen_reference中循环使用format!拼接字符串,每次都会创建新的String,触发频繁内存分配与拷贝。应该改用String::push,或者预先用with_capacity分配足够容量的字符串,避免扩容开销。decipher中同样用format!拼接解密结果,对于较长密文来说,每次循环的内存分配会累积成巨大开销,同样建议改用push直接追加字符。
2. score函数的冗余内存分配
- 每次循环都将四元组字节切片拷贝成
Vec<u8>再转成String,完全没必要。原文本是合法的大写ASCII字母,直接取&text[i..i+4]作为字符串切片,就能直接查询HashMap,省去拷贝和内存分配。
3. 线性查找的巨大开销
decipher中用iter().position()在字符数组中查找位置,每次都是O(n)的线性遍历。对于1400*1400次嵌套循环,每次解密还要多次执行这种查找,累计开销极大。建议为每个网格数组预先创建反向映射(比如[usize; 26],因为字母是固定的A-Z,J可以跳过),将查找变成O(1)的直接索引。
4. fill_grid的重复计算与冗余操作
- 同一个单词会在嵌套循环中被多次调用
fill_grid,可以用HashMap<String, [char;25]>缓存每个单词对应的网格结果,避免重复计算。 - 每次调用
letters()都会重新创建数组,可将其定义为全局常量,避免重复初始化。 fill_grid中修改临时字符串的操作可以优化:无需传入可变引用,直接对单词的字符去重后生成新的序列即可,避免不必要的字符串拷贝。
5. 其他细节优化
dictionary_attack中ciphertext.clone().len()完全没必要克隆字符串,直接用ciphertext.len()即可。scan_dict中分割字符串后手动去掉最后一个元素的做法不够稳健,应该在收集时过滤掉空字符串和换行符,比如用.filter(|s| !s.is_empty() && !s.contains('\n'))。
内容的提问来源于stack exchange,提问作者all_caps
相关产品推荐
相关产品推荐

