goddamn it is fast now

This commit is contained in:
2025-12-02 18:41:12 +01:00
parent e60f2bf319
commit da1f40e54f
4 changed files with 137 additions and 14 deletions

46
Cargo.lock generated
View File

@@ -798,6 +798,25 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
@@ -1002,6 +1021,12 @@ dependencies = [
"winit",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "emath"
version = "0.29.1"
@@ -2545,6 +2570,26 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rayon"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@@ -2627,6 +2672,7 @@ name = "rlogg"
version = "0.1.0"
dependencies = [
"eframe",
"rayon",
"regex",
"rfd",
"serde",

View File

@@ -16,3 +16,4 @@ rfd = "0.15"
regex = "1.11"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rayon = "1.10"

View File

@@ -51,4 +51,40 @@ impl LineIndex {
Some(line.trim_end_matches(['\r', '\n']).to_string())
}
/// Reads a range of lines efficiently by seeking once and reading sequentially
pub fn read_line_range(
&self,
file: &mut BufReader<File>,
start: usize,
end: usize,
) -> Vec<(usize, String)> {
let end = end.min(self.total_lines);
if start >= end || start >= self.total_lines {
return Vec::new();
}
let mut results = Vec::with_capacity(end - start);
// Seek to the start position once
let start_pos = self.positions[start];
if file.seek(SeekFrom::Start(start_pos)).is_err() {
return results;
}
// Read lines sequentially without additional seeks
let mut line = String::new();
for line_num in start..end {
line.clear();
if file.read_line(&mut line).is_err() {
break;
}
if line.is_empty() {
break;
}
results.push((line_num, line.trim_end_matches(['\r', '\n']).to_string()));
}
results
}
}

View File

@@ -1,6 +1,8 @@
use rayon::prelude::*;
use regex::Regex;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -100,31 +102,69 @@ pub fn start_search(
fn search_lines(
params: &SearchParams,
line_index: &LineIndex,
file_path: &std::path::Path,
file_path: &Path,
progress: &Arc<Mutex<f32>>,
) -> Vec<FilteredLine> {
let mut filtered = Vec::new();
let regex_matcher = params.build_regex_matcher();
let total_lines = line_index.total_lines;
if total_lines == 0 {
return Vec::new();
}
if let Ok(file) = File::open(file_path) {
let mut file_handle = BufReader::new(file);
let total_lines = line_index.total_lines;
// Determine optimal chunk size based on total lines
// Aim for enough chunks to utilize all cores, but not too many to avoid overhead
let num_threads = rayon::current_num_threads();
let min_chunk_size = 1000; // Process at least 1000 lines per chunk
let chunk_size = (total_lines / (num_threads * 4)).max(min_chunk_size);
for line_num in 0..total_lines {
if let Some(content) = line_index.read_line(&mut file_handle, line_num) {
// Split line numbers into chunks
let chunks: Vec<(usize, usize)> = (0..total_lines)
.step_by(chunk_size)
.map(|start| {
let end = (start + chunk_size).min(total_lines);
(start, end)
})
.collect();
let total_chunks = chunks.len();
let processed_chunks = Arc::new(Mutex::new(0usize));
// Process chunks in parallel
let results: Vec<Vec<FilteredLine>> = chunks
.par_iter()
.filter_map(|(start, end)| {
// Each thread opens its own file handle
let file = File::open(file_path).ok()?;
let mut file_handle = BufReader::new(file);
let regex_matcher = params.build_regex_matcher();
let mut chunk_results = Vec::new();
// Read lines in this chunk efficiently (one seek, sequential reads)
let lines = line_index.read_line_range(&mut file_handle, *start, *end);
// Process each line
for (line_number, content) in lines {
if params.matches_line(&content, &regex_matcher) {
filtered.push(FilteredLine {
line_number: line_num,
chunk_results.push(FilteredLine {
line_number,
content,
});
}
}
if line_num % 1000 == 0 {
*progress.lock().unwrap() = line_num as f32 / total_lines as f32;
// Update progress
{
let mut count = processed_chunks.lock().unwrap();
*count += 1;
*progress.lock().unwrap() = *count as f32 / total_chunks as f32;
}
}
}
Some(chunk_results)
})
.collect();
// Flatten and sort results by line number
let mut filtered: Vec<FilteredLine> = results.into_iter().flatten().collect();
filtered.sort_by_key(|f| f.line_number);
filtered
}