mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
sort: pre-compute ICU collation sort keys for fast locale sorting
Add a fast_locale_collation path that pre-computes ICU sort keys during line parsing (O(n)), then uses cheap byte-array comparison during sorting (O(n log n)). This replaces per-comparison ICU compare_utf8 calls for the common case of plain `sort` with a UTF-8 locale. With 1M lines and LC_ALL=en_US.UTF-8, this is ~2.6x faster than GNU sort (314ms vs 822ms).
This commit is contained in:
committed by
Daniel Hofstetter
parent
1f9b95d68d
commit
d830e84ec3
@@ -75,7 +75,7 @@ impl LineData<'_> {
|
||||
impl Chunk {
|
||||
/// Destroy this chunk and return its components to be reused.
|
||||
pub fn recycle(mut self) -> RecycledChunk {
|
||||
let recycled_contents = self.with_dependent_mut(|_, contents| {
|
||||
let mut recycled_contents = self.with_dependent_mut(|_, contents| {
|
||||
contents.lines.clear();
|
||||
contents.line_data.selections.clear();
|
||||
contents.line_data.num_infos.clear();
|
||||
@@ -100,7 +100,7 @@ impl Chunk {
|
||||
&mut contents.line_data.selections,
|
||||
))
|
||||
};
|
||||
(
|
||||
RecycledChunk {
|
||||
lines,
|
||||
selections,
|
||||
num_infos: std::mem::take(&mut contents.line_data.num_infos),
|
||||
|
||||
+25
-1
@@ -48,7 +48,7 @@ use uucore::error::{FromIo, strip_errno};
|
||||
use uucore::error::{UError, UResult, USimpleError, UUsageError};
|
||||
use uucore::extendedbigdecimal::ExtendedBigDecimal;
|
||||
#[cfg(feature = "i18n-collator")]
|
||||
use uucore::i18n::collator::locale_cmp;
|
||||
use uucore::i18n::collator::{compute_sort_key_utf8, locale_cmp};
|
||||
use uucore::i18n::decimal::locale_decimal_separator;
|
||||
use uucore::line_ending::LineEnding;
|
||||
use uucore::parser::num_parser::{ExtendedParser, ExtendedParserError};
|
||||
@@ -324,6 +324,7 @@ struct Precomputed {
|
||||
floats_per_line: usize,
|
||||
selections_per_line: usize,
|
||||
fast_lexicographic: bool,
|
||||
fast_locale_collation: bool,
|
||||
fast_ascii_insensitive: bool,
|
||||
tokenize_blank_thousands_sep: bool,
|
||||
tokenize_allow_unit_after_blank: bool,
|
||||
@@ -387,6 +388,8 @@ impl GlobalSettings {
|
||||
|
||||
self.precomputed.fast_lexicographic =
|
||||
!disable_fast_lexicographic && self.can_use_fast_lexicographic();
|
||||
self.precomputed.fast_locale_collation =
|
||||
disable_fast_lexicographic && self.can_use_fast_lexicographic();
|
||||
self.precomputed.fast_ascii_insensitive = self.can_use_fast_ascii_insensitive();
|
||||
}
|
||||
|
||||
@@ -632,6 +635,15 @@ impl<'a> Line<'a> {
|
||||
token_buffer: &mut Vec<Field>,
|
||||
settings: &GlobalSettings,
|
||||
) -> Self {
|
||||
#[cfg(feature = "i18n-collator")]
|
||||
if settings.precomputed.fast_locale_collation {
|
||||
compute_sort_key_utf8(line, &mut line_data.collation_key_buffer);
|
||||
line_data
|
||||
.collation_key_ends
|
||||
.push(line_data.collation_key_buffer.len());
|
||||
return Self { line, index };
|
||||
}
|
||||
|
||||
let needs_line_data = settings.precomputed.needs_tokens
|
||||
|| settings.precomputed.selections_per_line > 0
|
||||
|| settings.precomputed.num_infos_per_line > 0
|
||||
@@ -2614,6 +2626,18 @@ fn compare_by<'a>(
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "i18n-collator")]
|
||||
if global_settings.precomputed.fast_locale_collation {
|
||||
let a_key = a_line_data.collation_key(a.index);
|
||||
let b_key = b_line_data.collation_key(b.index);
|
||||
let cmp = a_key.cmp(b_key);
|
||||
return if global_settings.reverse {
|
||||
cmp.reverse()
|
||||
} else {
|
||||
cmp
|
||||
};
|
||||
}
|
||||
|
||||
if global_settings.precomputed.fast_ascii_insensitive {
|
||||
let cmp = ascii_case_insensitive_cmp(a.line, b.line);
|
||||
if cmp != Ordering::Equal || a.line == b.line {
|
||||
|
||||
Reference in New Issue
Block a user