Skip to content

Commit

Permalink
Remove fast_binary_search. rust-lang/rust#45333 merged its implementa…
Browse files Browse the repository at this point in the history
…tion to the standard library.
  • Loading branch information
alkis committed Nov 30, 2017
1 parent 89c8ce2 commit 3568346
Show file tree
Hide file tree
Showing 4 changed files with 4 additions and 164 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

[package]
name = "ordslice"
version = "0.1.0"
version = "0.2.0"
authors = ["Alkis Evlogimenos <[email protected]>"]
description = "Extensions for ordered slices"
homepage = "https://github.com/alkis/ordslice-rs"
Expand Down
17 changes: 2 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,11 @@ extern crate ordslice;
use ordslice::Ext;
```

Now you can enjoy super fast `lower_bound`, `upper_bound`, `equal_range`, and
`fast_binary_search`.

`fast_binary_search` is much faster than `binary_search`:

```diff,ignore
name std ns/iter fast ns/iter diff ns/iter diff % speedup
+l1::dups 31 10 -21 -67.74% x 3.10
+l1::unique 35 10 -25 -71.43% x 3.50
+l2::dups 54 19 -35 -64.81% x 2.84
+l2::unique 59 19 -40 -67.80% x 3.11
+l3::dups 131 81 -50 -38.17% x 1.62
+l3::unique 135 80 -55 -40.74% x 1.69
```
Now you can enjoy super fast `lower_bound`, `upper_bound`, and `equal_range`.

### Why isn't this part of the standard library?

Worry not, work is on the way:

- [ ] Make `binary_search` as fast as `fast_binary_search`: https://github.com/rust-lang/rust/pull/45333
- [X] Make `binary_search` as fast as ~~`fast_binary_search`~~: https://github.com/rust-lang/rust/pull/45333
- [ ] Add `lower_bound`, `upper_bound`, `equal_range` to std: https://github.com/rust-lang/rfcs/issues/2184
26 changes: 0 additions & 26 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,32 +106,6 @@ fn generate_inputs(cache: Cache, config: Config) -> (Vec<usize>, Vec<usize>) {
(values, lookups)
}

mod binary_search {
use super::*;
fn run(b: &mut Bencher, cache: Cache, config: Config) {
let (values, lookups) = generate_inputs(cache, config);
let mut iter = lookups.iter().cycle();
b.iter(|| {
values.binary_search(iter.next().unwrap()).is_ok()
})
}

for_each_cache!();
}

mod fast_binary_search {
use super::*;
fn run(b: &mut Bencher, cache: Cache, config: Config) {
let (values, lookups) = generate_inputs(cache, config);
let mut iter = lookups.iter().cycle();
b.iter(|| {
values.fast_binary_search(iter.next().unwrap()).is_ok()
})
}

for_each_cache!();
}

mod lower_bound {
use super::*;
fn run(b: &mut Bencher, cache: Cache, config: Config) {
Expand Down
123 changes: 1 addition & 122 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
//! use ordslice::Ext;
//!
//! let b = [1, 3];
//! // This is faster than stdlib's binary_search!
//! assert_eq!(b.fast_binary_search(&2), Err(1));
//! assert_eq!(b.fast_binary_search(&3), Ok(1));
//!
//! assert_eq!(b.lower_bound(&1), 0);
//!
Expand All @@ -32,80 +29,14 @@
//! ```
//!
//! [`slice`]: https://doc.rust-lang.org/stable/std/primitive.slice.html
use std::cmp::Ordering::{self, Less, Equal, Greater};
use std::cmp::Ordering::{self, Less, Greater};

/// Extends [`slice`] with fast operations on ordered slices.
///
/// [`slice`]: https://doc.rust-lang.org/stable/std/primitive.slice.html
pub trait Ext {
type Item;

/// Checks if `x` appears in the ordered slice.
///
/// Returns `Ok(i)` where `i` is the index of the matching element, `Err(i)`
/// otherwise where `i` is the index where the element should be inserted to
/// preserve the slice's ordering.
///
/// The slice MUST be ordered by the order defined by its elements.
///
/// Note: this is the same as [`binary_search`] but faster.
///
/// | name |std (ns) |fast (ns) |diff (ns) | diff (%) | speedup |
/// | -----------|---------|----------|----------|----------|---------|
/// | l1::dups | 31 | 10 | -21 | -67.74% | x 3.10 |
/// | l1::unique | 35 | 10 | -25 | -71.43% | x 3.50 |
/// | l2::dups | 54 | 19 | -35 | -64.81% | x 2.84 |
/// | l2::unique | 58 | 19 | -39 | -67.24% | x 3.05 |
/// | l3::dups | 136 | 82 | -54 | -39.71% | x 1.66 |
/// | l3::unique | 139 | 84 | -55 | -39.57% | x 1.65 |
///
/// [`binary_search`]:
/// https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search
fn fast_binary_search(&self, x: &Self::Item) -> Result<usize, usize>
where
Self::Item: Ord;

/// Check if there is an element `e` in the ordered slice such that `f(e) ==
/// Equal`.
///
/// The slice MUST be ordered by the order defined by the comparator
/// function. The comparator function should take an element and return
/// `Ordering` that is consistent with the ordering of the slice. Returns
/// `Ok(i)` where `i` is the index of the matching element, `Err(i)`
/// otherwise where `i` is the index where the element should be inserted to
/// preserve the slice's ordering.
///
/// # Example:
///
/// ```
/// # use ordslice::Ext;
/// let b = [1, 2, 3, 6, 9, 9];
/// assert_eq!(b.fast_binary_search(&3), b.fast_binary_search_by(|x| x.cmp(&3)));
/// ```
fn fast_binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
where
F: FnMut(&'a Self::Item) -> Ordering;

/// Check if there is an element `e` in the ordered slice such that `f(e) ==
/// k`.
///
/// The slice MUST be ordered by the order defined by the keys of its
/// elements. Returns `Ok(i)` where `i` is the index of the matching
/// element, `Err(i)` otherwise where `i` is the index where the element
/// should be inserted to preserve the slice's ordering.
///
/// # Example:
///
/// ```
/// # use ordslice::Ext;
/// let b = [1, 2, 3, 6, 9, 9];
/// assert_eq!(b.fast_binary_search(&3), b.fast_binary_search_by_key(&6, |x| x * 2));
/// ```
fn fast_binary_search_by_key<'a, K, F>(&'a self, k: &K, f: F) -> Result<usize, usize>
where
F: FnMut(&'a Self::Item) -> K,
K: Ord;

/// Returns the index `i` pointing to the first element in the ordered slice
/// that is _not less_ than `x`.
///
Expand Down Expand Up @@ -289,44 +220,6 @@ pub trait Ext {
impl<T> Ext for [T] {
type Item = T;

fn fast_binary_search(&self, x: &Self::Item) -> Result<usize, usize>
where
T: Ord,
{
self.fast_binary_search_by(|y| y.cmp(x))
}
fn fast_binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
where
F: FnMut(&'a Self::Item) -> Ordering,
{
let s = self;
let mut size = s.len();
if size == 0 {
return Err(0);
}
let mut base = 0usize;
while size > 1 {
let half = size / 2;
let mid = base + half;
let cmp = f(unsafe { s.get_unchecked(mid) });
base = if cmp == Greater { base } else { mid };
size -= half;
}
let cmp = f(unsafe { s.get_unchecked(base) });
if cmp == Equal {
Ok(base)
} else {
Err(base + (cmp == Less) as usize)
}
}
fn fast_binary_search_by_key<'a, K, F>(&'a self, k: &K, mut f: F) -> Result<usize, usize>
where
F: FnMut(&'a Self::Item) -> K,
K: Ord,
{
self.fast_binary_search_by(|e| f(e).cmp(k))
}

fn lower_bound(&self, x: &Self::Item) -> usize
where
T: Ord,
Expand Down Expand Up @@ -445,20 +338,6 @@ impl<T> Ext for [T] {
mod tests {
use super::Ext;

#[test]
fn binary_search() {
let b: [u32; 0] = [];
assert_eq!(b.fast_binary_search(&0), Err(0));
let b = [1, 3, 3, 5];
assert_eq!(b.fast_binary_search(&0), Err(0));
assert_eq!(b.fast_binary_search(&1), Ok(0));
assert_eq!(b.fast_binary_search(&2), Err(1));
assert_eq!(b.fast_binary_search(&3), Ok(2));
assert_eq!(b.fast_binary_search(&4), Err(3));
assert_eq!(b.fast_binary_search(&5), Ok(3));
assert_eq!(b.fast_binary_search(&6), Err(4));
}

#[test]
fn lower_bound() {
let b: [u32; 0] = [];
Expand Down

0 comments on commit 3568346

Please sign in to comment.