@@ -356,6 +356,8 @@ impl Searcher {
356356 equate_il : bool ,
357357 tryptic : bool
358358 ) -> SearchAllSuffixesResult {
359+ self . prefetch_kmer_range ( search_string) ;
360+
359361 // Cap pre-allocation at 4096 entries (32 KB) so callers passing large max_matches
360362 // don't wastefully over-allocate for peptides that match rarely.
361363 let mut matching_suffixes: Vec < i64 > = Vec :: with_capacity ( max_matches. min ( 4096 ) ) ;
@@ -366,19 +368,8 @@ impl Searcher {
366368 }
367369 }
368370
369- self . prefetch_kmer_range ( search_string) ;
370-
371371 let mut skip: usize = 0 ;
372372 while skip < self . sa . sample_rate ( ) as usize {
373- // Prefetch the SA range for the next skip value now, so the entire current
374- // iteration (search_bounds + SA range walk) serves as madvise lead time.
375- // madvise(MADV_WILLNEED) targets the OS page cache, not the CPU cache, so
376- // it won't be evicted by the CPU-level accesses in the current iteration.
377- let next_skip = skip + 1 ;
378- if next_skip < self . sa . sample_rate ( ) as usize {
379- self . prefetch_kmer_range ( & search_string[ next_skip..] ) ;
380- }
381-
382373 // il_locations is built in ascending index order, so partition_point gives us
383374 // the first position that is relevant for this skip value in O(log n).
384375 // These are still absolute positions within `search_string`, not within the suffix.
@@ -436,7 +427,14 @@ impl Searcher {
436427 }
437428 }
438429 }
430+
439431 skip += 1 ;
432+
433+ // Only prefetch skip+1's SA range after confirming we're not returning early.
434+ // Issuing madvise when skip+1 will never execute wastes a syscall
435+ if skip < self . sa . sample_rate ( ) as usize {
436+ self . prefetch_kmer_range ( & search_string[ skip..] ) ;
437+ }
440438 }
441439
442440 if matching_suffixes. is_empty ( ) {
0 commit comments