Reduce lock contention (round 1)

- Use an async (i.e. unlimited buffer) MPSC channel instead of an
  Arc<Mutex<Vec>> for storing the scored matches in Sorter
- Use Arc<Matcher> instead of Arc<Mutex<Matcher>> for the matcher, as
  it's not mutated and appears to be threadsafe.

This cuts average iteration time (on the benchmarked machine) from
25.98ms to 16.08ms for the ivy_files benchmark.
This commit is contained in:
Xymist 2022-08-26 16:01:22 +01:00
parent ce28b248fa
commit 7fb8be541a
3 changed files with 26 additions and 17 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ compile_commands.json
.luacheckcache
benchmarks
flamegraph*
perf.data*

View file

@ -66,18 +66,19 @@ pub extern "C" fn ivy_files(c_pattern: *const c_char, c_base_dir: *const c_char)
}
pub fn inner_files(pattern: String, base_dir: String) -> String {
// Bail out early if the pattern is empty its never going to find anything
let mut output = String::new();
// Bail out early if the pattern is empty; it's never going to find anything
if pattern.is_empty() {
return String::new();
return output;
}
let files = get_files(&base_dir);
let mut output = String::new();
let sorter_options = sorter::Options::new(pattern);
let files = sorter::sort_strings(sorter_options, files);
for file in files.lock().unwrap().iter() {
for file in files.iter() {
output.push_str(&file.content);
output.push('\n');
}

View file

@ -1,8 +1,8 @@
use super::matcher;
use super::thread_pool;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
pub struct Match {
pub score: i64,
@ -23,30 +23,37 @@ impl Options {
}
}
pub fn sort_strings(options: Options, strings: Vec<String>) -> Arc<Mutex<Vec<Match>>> {
let matches: Arc<Mutex<Vec<Match>>> = Arc::new(Mutex::new(Vec::new()));
let matcher = Arc::new(Mutex::new(matcher::Matcher::new(options.pattern)));
pub fn sort_strings(options: Options, strings: Vec<String>) -> Vec<Match> {
let mut matches = Vec::new();
let matcher = Arc::new(matcher::Matcher::new(options.pattern));
let pool = thread_pool::ThreadPool::new(std::thread::available_parallelism().unwrap().get());
let (tx, rx) = mpsc::channel::<Match>();
for string in strings {
let thread_matcher = Arc::clone(&matcher);
let thread_matches = Arc::clone(&matches);
let thread_transmitter = tx.clone();
pool.execute(move || {
let score = thread_matcher.lock().unwrap().score(string.to_string());
let score = thread_matcher.score(string.to_string());
if score > 25 {
let mut tmp = thread_matches.lock().unwrap();
let content = string.clone();
tmp.push(Match { score, content });
thread_transmitter
.send(Match {
score,
content: string,
})
.expect("Failed to push data to channel");
}
})
}
drop(pool);
drop(tx);
matches
.lock()
.unwrap()
.sort_by(|a, b| a.score.cmp(&b.score));
while let Ok(result) = rx.recv() {
matches.push(result)
}
matches.sort_by(|a, b| a.score.cmp(&b.score));
matches
}