Skip to content

Commit

Permalink
optimized SipHash implementation
Browse files Browse the repository at this point in the history
work started from @gereeter's PR: #13114
but adjusted bits
  • Loading branch information
seanmonstar committed Apr 15, 2014
1 parent 246ebd2 commit 9c1cd69
Showing 1 changed file with 143 additions and 42 deletions.
185 changes: 143 additions & 42 deletions src/libstd/hash/sip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
use clone::Clone;
use container::Container;
use default::Default;
use int;
use io::{IoResult, Writer};
use iter::Iterator;
use result::Ok;
use slice::ImmutableVector;
use uint;

use super::{Hash, Hasher};

Expand All @@ -43,7 +45,7 @@ pub struct SipState {
v1: u64,
v2: u64,
v3: u64,
tail: [u8, ..8], // unprocessed bytes
tail: u64, // unprocessed bytes le
ntail: uint, // how many bytes in tail are valid
}

Expand All @@ -60,7 +62,17 @@ macro_rules! u8to64_le (
$buf[4+$i] as u64 << 32 |
$buf[5+$i] as u64 << 40 |
$buf[6+$i] as u64 << 48 |
$buf[7+$i] as u64 << 56)
$buf[7+$i] as u64 << 56);
($buf:expr, $i:expr, $len:expr) =>
({
let mut t = 0;
let mut out = 0u64;
while t < $len {
out |= $buf[t+$i] as u64 << t*8;
t += 1;
}
out
});
)

macro_rules! rotl (
Expand Down Expand Up @@ -98,7 +110,7 @@ impl SipState {
v1: 0,
v2: 0,
v3: 0,
tail: [ 0, 0, 0, 0, 0, 0, 0, 0 ],
tail: 0,
ntail: 0,
};
state.reset();
Expand All @@ -124,15 +136,7 @@ impl SipState {
let mut v2 = self.v2;
let mut v3 = self.v3;

let mut b : u64 = (self.length as u64 & 0xff) << 56;

if self.ntail > 0 { b |= self.tail[0] as u64 << 0; }
if self.ntail > 1 { b |= self.tail[1] as u64 << 8; }
if self.ntail > 2 { b |= self.tail[2] as u64 << 16; }
if self.ntail > 3 { b |= self.tail[3] as u64 << 24; }
if self.ntail > 4 { b |= self.tail[4] as u64 << 32; }
if self.ntail > 5 { b |= self.tail[5] as u64 << 40; }
if self.ntail > 6 { b |= self.tail[6] as u64 << 48; }
let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;

v3 ^= b;
compress!(v0, v1, v2, v3);
Expand All @@ -147,8 +151,38 @@ impl SipState {

v0 ^ v1 ^ v2 ^ v3
}

#[inline]
fn write_le(&mut self, n: u64, size: uint) {
self.tail |= n << 8*self.ntail;
self.ntail += size;

if self.ntail >= 8 {
let m = self.tail;

self.v3 ^= m;
compress!(self.v0, self.v1, self.v2, self.v3);
compress!(self.v0, self.v1, self.v2, self.v3);
self.v0 ^= m;

self.ntail -= 8;
if self.ntail == 0 {
self.tail = 0;
} else {
self.tail = n >> 64 - 8*self.ntail;
}
}
}
}

macro_rules! make_write_le(
($this:expr, $n:expr, $size:expr) => ({
$this.write_le($n as u64, $size);
$this.length += $size;
Ok(())
})
)

impl Writer for SipState {
#[inline]
fn write(&mut self, msg: &[u8]) -> IoResult<()> {
Expand All @@ -159,24 +193,13 @@ impl Writer for SipState {

if self.ntail != 0 {
needed = 8 - self.ntail;

if length < needed {
let mut t = 0;
while t < length {
self.tail[self.ntail+t] = msg[t];
t += 1;
}
self.tail |= u8to64_le!(msg, 0, length) << 8*self.ntail;
self.ntail += length;
return Ok(());
}

let mut t = 0;
while t < needed {
self.tail[self.ntail+t] = msg[t];
t += 1;
}

let m = u8to64_le!(self.tail, 0);
let m = self.tail | u8to64_le!(msg, 0, needed) << 8*self.ntail;

self.v3 ^= m;
compress!(self.v0, self.v1, self.v2, self.v3);
Expand All @@ -203,15 +226,62 @@ impl Writer for SipState {
i += 8;
}

let mut t = 0u;
while t < left {
self.tail[t] = msg[i+t];
t += 1
}
self.tail = u8to64_le!(msg, i, left);
self.ntail = left;

Ok(())
}

#[inline]
fn write_u8(&mut self, n: u8) -> IoResult<()> {
make_write_le!(self, n, 1)
}

#[inline]
fn write_le_u16(&mut self, n: u16) -> IoResult<()> {
make_write_le!(self, n, 2)
}

#[inline]
fn write_le_u32(&mut self, n: u32) -> IoResult<()> {
make_write_le!(self, n, 4)
}

#[inline]
fn write_le_u64(&mut self, n: u64) -> IoResult<()> {
make_write_le!(self, n, 8)
}

#[inline]
fn write_le_uint(&mut self, n: uint) -> IoResult<()> {
make_write_le!(self, n, uint::BYTES)
}

#[inline]
fn write_i8(&mut self, n: i8) -> IoResult<()> {
make_write_le!(self, n, 1)
}

#[inline]
fn write_le_i16(&mut self, n: i16) -> IoResult<()> {
make_write_le!(self, n, 2)
}

#[inline]
fn write_le_i32(&mut self, n: i32) -> IoResult<()> {
make_write_le!(self, n, 4)
}

#[inline]
fn write_le_i64(&mut self, n: i64) -> IoResult<()> {
make_write_le!(self, n, 8)
}

#[inline]
fn write_le_int(&mut self, n: int) -> IoResult<()> {
make_write_le!(self, n, int::BYTES)
}

}

impl Clone for SipState {
Expand Down Expand Up @@ -284,6 +354,8 @@ pub fn hash_with_keys<T: Hash<SipState>>(k0: u64, k1: u64, value: &T) -> u64 {
state.result()
}



#[cfg(test)]
mod tests {
extern crate test;
Expand Down Expand Up @@ -517,28 +589,57 @@ mod tests {
}

#[bench]
fn bench_str(b: &mut Bencher) {
fn bench_str_under_8_bytes(b: &mut Bencher) {
let s = "foo";
b.iter(|| {
assert_eq!(hash(&s), 16262950014981195938);
})
}

#[bench]
fn bench_str_of_8_bytes(b: &mut Bencher) {
let s = "foobar78";
b.iter(|| {
assert_eq!(hash(&s), 4898293253460910787);
})
}

#[bench]
fn bench_str_over_8_bytes(b: &mut Bencher) {
let s = "foobarbaz0";
b.iter(|| {
assert_eq!(hash(&s), 10581415515220175264);
})
}

#[bench]
fn bench_long_str(b: &mut Bencher) {
let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor \
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute \
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \
officia deserunt mollit anim id est laborum.";
b.iter(|| {
assert_eq!(hash(&s), 17717065544121360093);
})
}

#[bench]
fn bench_u64(b: &mut Bencher) {
let u = 16262950014981195938u64;
b.iter(|| {
assert_eq!(hash(&u), 5254097107239593357);
})
}

#[deriving(Hash)]
struct Compound {
x: u8,
y: u16,
y: u64,
z: ~str,
}

impl<S: Writer> Hash<S> for Compound {
#[inline]
fn hash(&self, state: &mut S) {
self.x.hash(state);
self.y.hash(state);
self.z.hash(state);
}
}

#[bench]
fn bench_compound_1(b: &mut Bencher) {
let compound = Compound {
Expand All @@ -547,7 +648,7 @@ mod tests {
z: ~"foobarbaz",
};
b.iter(|| {
assert_eq!(hash(&compound), 3581836382593270478);
assert_eq!(hash(&compound), 15783192367317361799);
})
}
}

5 comments on commit 9c1cd69

@bors
Copy link
Contributor

@bors bors commented on 9c1cd69 Apr 16, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from alexcrichton
at seanmonstar@9c1cd69

@bors
Copy link
Contributor

@bors bors commented on 9c1cd69 Apr 16, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging seanmonstar/rust/sip = 9c1cd69 into auto

@bors
Copy link
Contributor

@bors bors commented on 9c1cd69 Apr 16, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seanmonstar/rust/sip = 9c1cd69 merged ok, testing candidate = e332287

@bors
Copy link
Contributor

@bors bors commented on 9c1cd69 Apr 16, 2014

@bors
Copy link
Contributor

@bors bors commented on 9c1cd69 Apr 16, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = e332287

Please sign in to comment.