about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-18 18:54:54 +0000
committerbors <bors@rust-lang.org>2024-01-18 18:54:54 +0000
commit25f8d01fd8bda339612d0c0a8844173a09205f7c (patch)
tree168ed76d3740138f6680caedd0ffb4d0bbea4a72
parent8424f8e8cdf07010967a57584fd647b30e930d4d (diff)
parent97bc528877b698df2f61dd743b1a155ca3f5eead (diff)
downloadrust-25f8d01fd8bda339612d0c0a8844173a09205f7c.tar.gz
rust-25f8d01fd8bda339612d0c0a8844173a09205f7c.zip
Auto merge of #114231 - ttsugriy:binary_search_slice, r=cjgillot
[rustc_data_structures] Use partition_point to find  slice range end.

This PR uses approach introduced in https://github.com/rust-lang/rust/pull/114152 to find
the end of the range. It's much easier to understand and reason about invariants of such
implementation.
Technically it's possible to make it even shorter by returning `&[start..end]` unconditionally
because even if searched item is not present in the slice, `start` and `end` would point at
the same index, so the range would be empty. The reason I decided not to use this shorter
implementation is because it would involve more comparisons in case there are no elements
in the slice with key equal to `key`.

Also, not that it matters much, but this implementation also improves perf according to the
benchmark below:
https://gist.github.com/ttsugriy/63c0ed39ae132b131931fa1f8a3dea55

The results on my M1 macbook air are:
```
Running benches/bin_search_slice_benchmark.rs (target/release/deps/bin_search_slice_benchmark-90fa6d68c3bd1298)
Benchmarking multiply add/binary_search_slice: Collecting 100 samples in estimated 5.0002 s (1
multiply add/binary_search_slice
                        time:   [44.719 ns 44.918 ns 45.158 ns]
                        No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
  1 (1.00%) high mild
  2 (2.00%) high severe
Benchmarking multiply add/binary_search_slice_new: Collecting 100 samples in estimated 5.0001
multiply add/binary_search_slice_new
                        time:   [36.955 ns 37.060 ns 37.221 ns]
                        No change in performance detected.
Found 7 outliers among 100 measurements (7.00%)
  3 (3.00%) high mild
  4 (4.00%) high severe
```
-rw-r--r--compiler/rustc_data_structures/src/binary_search_util/mod.rs25
1 files changed, 4 insertions, 21 deletions
diff --git a/compiler/rustc_data_structures/src/binary_search_util/mod.rs b/compiler/rustc_data_structures/src/binary_search_util/mod.rs
index bc8a6b9eac0..1c6e227cec4 100644
--- a/compiler/rustc_data_structures/src/binary_search_util/mod.rs
+++ b/compiler/rustc_data_structures/src/binary_search_util/mod.rs
@@ -18,27 +18,10 @@ where
         return &[];
     };
 
-    // Now search forward to find the *last* one.
-    let mut end = start;
-    let mut previous = start;
-    let mut step = 1;
-    loop {
-        end = end.saturating_add(step).min(size);
-        if end == size || key_fn(&data[end]) != *key {
-            break;
-        }
-        previous = end;
-        step *= 2;
-    }
-    step = end - previous;
-    while step > 1 {
-        let half = step / 2;
-        let mid = end - half;
-        if key_fn(&data[mid]) != *key {
-            end = mid;
-        }
-        step -= half;
-    }
+    // Find the first entry with key > `key`. Skip `start` entries since
+    // key_fn(&data[start]) == *key
+    let offset = start + 1;
+    let end = data[offset..].partition_point(|x| key_fn(x) <= *key) + offset;
 
     &data[start..end]
 }