Skip to content

Commit 1b1aeed

Browse files
committed
perf: Packed byte layout for file item (reduce ram usage)
1 parent 29e6480 commit 1b1aeed

21 files changed

Lines changed: 307 additions & 231 deletions

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/fff-c/src/ffi_types.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,16 @@ pub struct FffFileItem {
7979
impl From<&FileItem> for FffFileItem {
8080
fn from(item: &FileItem) -> Self {
8181
FffFileItem {
82-
path: cstring_new(&item.path.to_string_lossy()),
83-
relative_path: cstring_new(&item.relative_path),
84-
file_name: cstring_new(&item.file_name),
82+
path: cstring_new(item.path_str()),
83+
relative_path: cstring_new(item.relative_path()),
84+
file_name: cstring_new(item.file_name()),
8585
git_status: cstring_new(format_git_status(item.git_status)),
8686
size: item.size,
8787
modified: item.modified,
8888
access_frecency_score: item.access_frecency_score as i64,
8989
modification_frecency_score: item.modification_frecency_score as i64,
90-
total_frecency_score: item.total_frecency_score as i64,
91-
is_binary: item.is_binary,
90+
total_frecency_score: item.total_frecency_score() as i64,
91+
is_binary: item.is_binary(),
9292
}
9393
}
9494
}
@@ -312,17 +312,17 @@ impl FffGrepMatch {
312312
};
313313

314314
FffGrepMatch {
315-
path: cstring_new(&file.path.to_string_lossy()),
316-
relative_path: cstring_new(&file.relative_path),
317-
file_name: cstring_new(&file.file_name),
315+
path: cstring_new(file.path_str()),
316+
relative_path: cstring_new(file.relative_path()),
317+
file_name: cstring_new(file.file_name()),
318318
git_status: cstring_new(format_git_status(file.git_status)),
319319
line_content: cstring_new(&m.line_content),
320320
match_ranges,
321321
context_before,
322322
context_after,
323323
size: file.size,
324324
modified: file.modified,
325-
total_frecency_score: file.total_frecency_score as i64,
325+
total_frecency_score: file.total_frecency_score() as i64,
326326
access_frecency_score: file.access_frecency_score as i64,
327327
modification_frecency_score: file.modification_frecency_score as i64,
328328
line_number: m.line_number,
@@ -333,7 +333,7 @@ impl FffGrepMatch {
333333
context_after_count,
334334
fuzzy_score,
335335
has_fuzzy_score,
336-
is_binary: file.is_binary,
336+
is_binary: file.is_binary(),
337337
is_definition: m.is_definition,
338338
}
339339
}

crates/fff-core/src/background_watcher.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,9 @@ fn handle_debounced_events(
333333
debug!(
334334
"on_create_or_modify({:?}) -> Some({})",
335335
path,
336-
file.path.display()
336+
file.path_str()
337337
);
338-
files_to_update.push(file.path.clone());
338+
files_to_update.push(PathBuf::from(file.path_str()));
339339
}
340340
None => {
341341
error!("on_create_or_modify({:?}) -> None (file not added!)", path);

crates/fff-core/src/file_picker.rs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,15 @@ impl FileSync {
147147
/// Find file index by path using binary search on the sorted base portion.
148148
#[inline]
149149
fn find_file_index(&self, path: &Path) -> Result<usize, usize> {
150-
self.files[..self.base_count].binary_search_by(|f| f.path.as_path().cmp(path))
150+
self.files[..self.base_count].binary_search_by(|f| f.as_path().cmp(path))
151151
}
152152

153153
/// Find a file in the overflow portion by path (linear scan).
154154
/// Returns the absolute index into `files`.
155155
fn find_overflow_index(&self, path: &Path) -> Option<usize> {
156156
self.files[self.base_count..]
157157
.iter()
158-
.position(|f| f.path == path)
158+
.position(|f| f.as_path() == path)
159159
.map(|pos| self.base_count + pos)
160160
}
161161

@@ -199,7 +199,7 @@ impl FileSync {
199199
/// Insert a file in sorted order (by path).
200200
/// Returns true if inserted, false if file already exists.
201201
fn insert_file_sorted(&mut self, file: FileItem) -> bool {
202-
match self.find_file_index(&file.path) {
202+
match self.find_file_index(file.as_path()) {
203203
Ok(_) => false, // File already exists
204204
Err(position) => {
205205
self.insert_file(position, file);
@@ -227,12 +227,6 @@ impl FileItem {
227227
.to_string_lossy()
228228
.into_owned();
229229

230-
let name = path
231-
.file_name()
232-
.unwrap_or_default()
233-
.to_string_lossy()
234-
.into_owned();
235-
236230
let (size, modified) = match metadata {
237231
Some(metadata) => {
238232
let size = metadata.len();
@@ -251,10 +245,17 @@ impl FileItem {
251245
// Files not caught here are detected when content is first loaded.
252246
let is_binary = is_known_binary_extension(&path);
253247

248+
let path_string = path.to_string_lossy().into_owned();
249+
let relative_start = (path_string.len() - relative_path.len()) as u16;
250+
let filename_start = path_string
251+
.rfind(std::path::MAIN_SEPARATOR)
252+
.map(|i| i + 1)
253+
.unwrap_or(relative_start as usize) as u16;
254+
254255
Self::new_raw(
255-
path,
256-
relative_path,
257-
name,
256+
path_string,
257+
relative_start,
258+
filename_start,
258259
size,
259260
modified,
260261
git_status,
@@ -267,10 +268,9 @@ impl FileItem {
267268
tracker: &FrecencyTracker,
268269
mode: FFFMode,
269270
) -> Result<(), Error> {
270-
self.access_frecency_score = tracker.get_access_score(&self.path, mode) as i32;
271+
self.access_frecency_score = tracker.get_access_score(self.as_path(), mode) as i16;
271272
self.modification_frecency_score =
272-
tracker.get_modification_score(self.modified, self.git_status, mode) as i32;
273-
self.total_frecency_score = self.access_frecency_score + self.modification_frecency_score;
273+
tracker.get_modification_score(self.modified, self.git_status, mode) as i16;
274274

275275
Ok(())
276276
}
@@ -499,7 +499,7 @@ impl FilePicker {
499499
// Apply git status synchronously.
500500
if let Ok(Some(git_cache)) = walk.git_handle.join() {
501501
for file in self.sync_data.files.iter_mut() {
502-
file.git_status = git_cache.lookup_status(&file.path);
502+
file.git_status = git_cache.lookup_status(file.as_path());
503503
}
504504
}
505505

@@ -733,7 +733,7 @@ impl FilePicker {
733733

734734
/// Add a file to the picker's files in sorted order (used by background watcher)
735735
pub fn add_file_sorted(&mut self, file: FileItem) -> Option<&FileItem> {
736-
let path = file.path.clone();
736+
let path = PathBuf::from(file.path_str());
737737

738738
if self.sync_data.insert_file_sorted(file) {
739739
// File was inserted, look it up
@@ -764,9 +764,9 @@ impl FilePicker {
764764
if let Ok(pos) = self.sync_data.find_file_index(path) {
765765
let file = self.sync_data.get_file_mut(pos)?;
766766

767-
if file.is_deleted {
767+
if file.is_deleted() {
768768
// Resurrect tombstoned file.
769-
file.is_deleted = false;
769+
file.set_deleted(false);
770770
debug!(
771771
"on_create_or_modify: resurrected tombstoned file at index {}",
772772
pos
@@ -856,7 +856,7 @@ impl FilePicker {
856856
match self.sync_data.find_file_index(path) {
857857
Ok(index) => {
858858
let file = &mut self.sync_data.files[index];
859-
file.is_deleted = true;
859+
file.set_deleted(true);
860860
file.invalidate_mmap(&self.cache_budget);
861861
if let Some(ref overlay) = self.bigram_overlay {
862862
overlay.write().delete_file(index);
@@ -885,7 +885,7 @@ impl FilePicker {
885885
let dir_path = dir.as_ref();
886886
// Use the safe retain_files method which maintains both indices
887887
self.sync_data
888-
.retain_files(|file| !file.path.starts_with(dir_path))
888+
.retain_files(|file| !file.as_path().starts_with(dir_path))
889889
}
890890

891891
/// Use this to prevent any substantial background threads from acquiring the locks
@@ -932,7 +932,7 @@ impl FilePicker {
932932
let mode = self.mode;
933933
BACKGROUND_THREAD_POOL.install(|| {
934934
self.sync_data.files.par_iter_mut().for_each(|file| {
935-
file.git_status = git_cache.lookup_status(&file.path);
935+
file.git_status = git_cache.lookup_status(file.as_path());
936936
if let Some(frecency) = frecency_ref {
937937
let _ = file.update_frecency_scores(frecency, mode);
938938
}
@@ -1155,7 +1155,7 @@ fn spawn_scan_and_watcher(
11551155
{
11561156
for &idx in &content_binary {
11571157
if let Some(file) = picker.sync_data.get_file_mut(idx) {
1158-
file.is_binary = true;
1158+
file.set_binary(true);
11591159
}
11601160
}
11611161

@@ -1203,13 +1203,13 @@ pub fn warmup_mmaps(files: &[FileItem], budget: &ContentCacheBudget) {
12031203
// they naturally sink past the partition boundary.
12041204
if all.len() > max_files {
12051205
all.select_nth_unstable_by(max_files, |a, b| {
1206-
let a_ok = !a.is_binary && a.size > 0;
1207-
let b_ok = !b.is_binary && b.size > 0;
1206+
let a_ok = !a.is_binary() && a.size > 0;
1207+
let b_ok = !b.is_binary() && b.size > 0;
12081208
match (a_ok, b_ok) {
12091209
(true, false) => std::cmp::Ordering::Less,
12101210
(false, true) => std::cmp::Ordering::Greater,
12111211
(false, false) => std::cmp::Ordering::Equal,
1212-
(true, true) => b.total_frecency_score.cmp(&a.total_frecency_score),
1212+
(true, true) => b.total_frecency_score().cmp(&a.total_frecency_score()),
12131213
}
12141214
});
12151215
}
@@ -1225,7 +1225,7 @@ pub fn warmup_mmaps(files: &[FileItem], budget: &ContentCacheBudget) {
12251225
return;
12261226
}
12271227

1228-
if file.is_binary || file.size == 0 || file.size > max_file_size {
1228+
if file.is_binary() || file.size == 0 || file.size > max_file_size {
12291229
return;
12301230
}
12311231

@@ -1265,7 +1265,7 @@ pub fn build_bigram_index(
12651265

12661266
BACKGROUND_THREAD_POOL.install(|| {
12671267
files.par_iter().enumerate().for_each(|(i, file)| {
1268-
if file.is_binary || file.size == 0 || file.size > max_file_size {
1268+
if file.is_binary() || file.size == 0 || file.size > max_file_size {
12691269
return;
12701270
}
12711271
// Use cached content if available (no extra memory).
@@ -1279,7 +1279,7 @@ pub fn build_bigram_index(
12791279
}
12801280
data = Some(cached);
12811281
owned = None;
1282-
} else if let Ok(read_data) = std::fs::read(&file.path) {
1282+
} else if let Ok(read_data) = std::fs::read(file.as_path()) {
12831283
if detect_binary_content(&read_data) {
12841284
content_binary.lock().unwrap().push(i);
12851285
return;
@@ -1389,7 +1389,7 @@ pub fn scan_files(base_path: &Path) -> Vec<FileItem> {
13891389
});
13901390

13911391
let mut files = files.into_inner();
1392-
files.sort_unstable_by(|a, b| a.path.as_os_str().cmp(b.path.as_os_str()));
1392+
files.sort_unstable_by(|a, b| a.path_str().cmp(b.path_str()));
13931393
files
13941394
}
13951395

@@ -1522,7 +1522,7 @@ fn walk_filesystem(
15221522
drop(frecency);
15231523

15241524
BACKGROUND_THREAD_POOL.install(|| {
1525-
files.par_sort_unstable_by(|a, b| a.path.as_os_str().cmp(b.path.as_os_str()));
1525+
files.par_sort_unstable_by(|a, b| a.path_str().cmp(b.path_str()));
15261526
});
15271527

15281528
let total_time = scan_start.elapsed();
@@ -1567,7 +1567,7 @@ fn apply_git_status(
15671567

15681568
BACKGROUND_THREAD_POOL.install(|| {
15691569
picker.sync_data.files.par_iter_mut().for_each(|file| {
1570-
file.git_status = git_cache.lookup_status(&file.path);
1570+
file.git_status = git_cache.lookup_status(file.as_path());
15711571
if let Some(frecency) = frecency_ref {
15721572
let _ = file.update_frecency_scores(frecency, mode);
15731573
}

0 commit comments

Comments
 (0)