From 29e1231eefba47e2f1ca21e28ced66ae9365df1e Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Thu, 15 Nov 2012 09:02:20 +1000 Subject: [PATCH] Add component-wise map, map2 and fold performance tests --- src/test/performance/vector_map_fold.rs | 183 ++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 src/test/performance/vector_map_fold.rs diff --git a/src/test/performance/vector_map_fold.rs b/src/test/performance/vector_map_fold.rs new file mode 100644 index 0000000..c61bfb5 --- /dev/null +++ b/src/test/performance/vector_map_fold.rs @@ -0,0 +1,183 @@ +/** + * Component-wise map and fold function speed tests. For best results, compile + * with the optimise flag (-O). These functions would allow for even more generic + * operations on dimensional data structures. Map seems to be faster than hand + * unrolling for add_t, but map2 for add_v is slower. A combination of map2 and + * foldl is faster for dot product. + */ + +extern mod std; +use std::time::precise_time_ns; +use cast::transmute; +use vec::raw::buf_as_slice; +use ptr::to_unsafe_ptr; +use cmp::Eq; +use num::from_int; + +pub struct Vec4 { x: T, y: T, z: T, w: T } + +pub mod Vec4 { + #[inline(always)] + pub pure fn new(x: T, y: T, z: T, w: T) -> Vec4 { + Vec4 { x: move x, y: move y, z: move z, w: move w } + } +} + +pub impl Vec4 { + #[inline(always)] + pure fn index(i: uint) -> T { + unsafe { do buf_as_slice( + transmute::<*Vec4, *T>( + to_unsafe_ptr(&self)), 4) |slice| { slice[i] } + } + } + + //////////////////////////////////////////////////////////////////////////// + + #[inline(always)] + pure fn map(f: fn&(a: &T) -> T) -> Vec4 { + Vec4::new(f(&self[0]), + f(&self[1]), + f(&self[2]), + f(&self[3])) + } + + #[inline(always)] + pure fn map2(other: &Vec4, f: fn&(a: &T, b: &T) -> T) -> Vec4 { + Vec4::new(f(&self[0], &other[0]), + f(&self[1], &other[1]), + f(&self[2], &other[2]), + f(&self[3], &other[3])) + } + + pure fn foldl(z: U, p: &fn(t: T, u: &U) -> U) -> U { + p(self[3], &p(self[2], &p(self[1], &p(self[0], &z)))) + } + pure fn foldr(z: U, p: &fn(t: &T, u: U) -> U) -> U { + p(&self[0], p(&self[1], p(&self[2], p(&self[3], z)))) + } + + //////////////////////////////////////////////////////////////////////////// + + #[inline(always)] + pure fn mul_t(value: T) -> Vec4 { + Vec4::new(self[0] * value, + self[1] * value, + self[2] * value, + self[3] * value) + } + + #[inline(always)] + pure fn mul_t_map(value: T) -> Vec4 { + do self.map |a| { a * value } + } + + #[inline(always)] + pure fn add_v(other: &Vec4) -> Vec4 { + Vec4::new(self[0] + other[0], + self[1] + other[1], + self[2] + other[2], + self[3] + other[3]) + } + + #[inline(always)] + pure fn add_v_map2(other: &Vec4) -> Vec4 { + do self.map2(other) |a, b| { a + *b } + } + + #[inline(always)] + pure fn dot(other: &Vec4) -> T { + self[0] * other[0] + + self[1] * other[1] + + self[2] * other[2] + + self[3] * other[3] + } + + #[inline(always)] + pure fn dot_foldl(other: &Vec4) -> T { + self.map2(other, |a, b| { a * *b }) + .foldl(from_int(0), |t, u| { t + *u }) + } +} + +pub impl Vec4: Eq { + #[inline(always)] + pure fn eq(other: &Vec4) -> bool { + self[0] == other[0] && + self[1] == other[1] && + self[2] == other[2] && + self[3] == other[3] + } + + #[inline(always)] + pure fn ne(other: &Vec4) -> bool { + !(self == *other) + } +} + +fn main() { + let n_tests = 10000; + + // Map + + let a = Vec4::new(1f, 2f, 3f, 4f); + let b = Vec4::new(5f, 6f, 7f, 8f); + + let mul_t_avg = do test_avg_time_ns(n_tests) { + assert a.mul_t(8f) == Vec4::new(8f, 16f, 24f, 32f); + }; + + let mul_t_map_avg = do test_avg_time_ns(n_tests) { + assert a.mul_t_map(8f) == Vec4::new(8f, 16f, 24f, 32f); + }; + + let min = [mul_t_avg, mul_t_map_avg].min(); + + io::println(fmt!("mul_t: %d = %d", mul_t_avg as int, (mul_t_avg - min) as int)); + io::println(fmt!("mul_t_map: %d = %d", mul_t_map_avg as int, (mul_t_map_avg - min) as int)); + + // Zip + + let add_v_avg = do test_avg_time_ns(n_tests) { + assert a.add_v(&b) == Vec4::new( 6f, 8f, 10f, 12f); + }; + + let add_v_map2_avg = do test_avg_time_ns(n_tests) { + assert a.add_v_map2(&b) == Vec4::new( 6f, 8f, 10f, 12f); + }; + + let min = [add_v_avg, add_v_map2_avg].min(); + + io::println(fmt!("add_v: %d = %d", add_v_avg as int, (add_v_avg - min) as int)); + io::println(fmt!("add_v_map2: %d = %d", add_v_map2_avg as int, (add_v_map2_avg - min) as int)); + + // Dot + + let dot_avg = do test_avg_time_ns(n_tests) { + assert a.dot(&b) == 70f; + }; + + let dot_foldl_avg = do test_avg_time_ns(n_tests) { + assert a.dot_foldl(&b) == 70f; + }; + + let min = [dot_avg, dot_foldl_avg].min(); + + io::println(fmt!("dot: %d = %d", dot_avg as int, (dot_avg - min) as int)); + io::println(fmt!("dot_foldl: %d = %d", dot_foldl_avg as int, (dot_foldl_avg - min) as int)); + +} + +fn test_avg_time_ns(n: uint, f: fn&()) -> u64 { + + let mut total = 0; + for n.times { + let start_time = precise_time_ns(); + + f(); + + total += precise_time_ns() - start_time; + } + + return total / (n as u64); +} \ No newline at end of file