move iter to cursor; cleanup iter benchmarks

without.crypto
Dan Burkert 10 years ago
parent 8016ebad3b
commit 6b00d64e5b
  1. 109
      src/cursor.rs
  2. 9
      src/transaction.rs

@ -38,6 +38,11 @@ pub trait ReadCursor<'txn> : Cursor<'txn> {
Ok((key_out, data_out)) Ok((key_out, data_out))
} }
} }
/// Open a new read-only cursor on the given database.
fn iter<'t>(&'t mut self) -> Items<'t> {
Items::new(self)
}
} }
pub trait WriteCursor<'txn> : ReadCursor<'txn> { pub trait WriteCursor<'txn> : ReadCursor<'txn> {
@ -183,15 +188,9 @@ pub struct Items<'txn> {
impl <'txn> Items<'txn> { impl <'txn> Items<'txn> {
/// Creates a new read-only cursor in the given database and transaction. Prefer using /// Creates a new iterator backed by the given cursor.
/// `WriteTransaction::open_write_cursor()`. fn new<'t>(cursor: &Cursor<'t>) -> Items<'t> {
pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult<Items<'txn>> { Items { cursor: cursor.cursor(), op: ffi::MDB_FIRST, next_op: ffi::MDB_NEXT }
let mut cursor: *mut MDB_cursor = ptr::null_mut();
unsafe {
// Create the cursor
try!(lmdb_result(mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor)));
}
Ok(Items { cursor: cursor, op: ffi::MDB_FIRST, next_op: ffi::MDB_NEXT })
} }
} }
@ -203,13 +202,13 @@ impl <'txn> Iterator<(&'txn [u8], &'txn [u8])> for Items<'txn> {
unsafe { unsafe {
let err_code = ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op); let err_code = ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op);
// Seek to the next item // Set the operation for the next get
self.op = self.next_op; self.op = self.next_op;
if err_code == ffi::MDB_SUCCESS { if err_code == ffi::MDB_SUCCESS {
Some((val_to_slice(key), val_to_slice(data))) Some((val_to_slice(key), val_to_slice(data)))
} else { } else {
// The documentation says that mdb_cursor_get may fail with MDB_NOTFOUND and MDB_EINVAL // The documentation for mdb_cursor_get specifies that it may fail with MDB_NOTFOUND
// (and we shouldn't be passing in invalid parameters). // and MDB_EINVAL (and we shouldn't be passing in invalid parameters).
// TODO: validate that these are the only failures possible. // TODO: validate that these are the only failures possible.
debug_assert!(err_code == ffi::MDB_NOTFOUND, debug_assert!(err_code == ffi::MDB_NOTFOUND,
"Unexpected LMDB error {}.", LmdbError::from_err_code(err_code)); "Unexpected LMDB error {}.", LmdbError::from_err_code(err_code));
@ -233,7 +232,7 @@ mod test {
use transaction::*; use transaction::*;
#[test] #[test]
fn test_items() { fn test_iter() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
@ -247,7 +246,8 @@ mod test {
} }
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
let iter = txn.iter(db).unwrap(); let mut cursor = txn.open_read_cursor(db).unwrap();
let iter = cursor.iter();
let items: Vec<(&[u8], &[u8])> = iter.collect(); let items: Vec<(&[u8], &[u8])> = iter.collect();
assert_eq!(vec!((b"key1", b"val1"), assert_eq!(vec!((b"key1", b"val1"),
@ -256,7 +256,6 @@ mod test {
items); items);
} }
#[test] #[test]
fn test_get() { fn test_get() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
@ -447,94 +446,92 @@ mod test {
cursor.get(None, None, MDB_LAST).unwrap()); cursor.get(None, None, MDB_LAST).unwrap());
} }
fn get_bench_ctx<'a>(num_rows: u32) -> (io::TempDir, Environment) {
#[bench]
fn bench_items(b: &mut Bencher) {
let n = 100u32;
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap();
{ {
let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_write_txn().unwrap();
for i in range(0, n) { for i in range(0, num_rows) {
txn.put(db, format!("key{}", i).as_bytes(), format!("val{}", i).as_bytes(), WriteFlags::empty()).unwrap(); txn.put(db, format!("key{}", i).as_bytes(), format!("val{}", i).as_bytes(), WriteFlags::empty()).unwrap();
} }
txn.commit().unwrap(); txn.commit().unwrap();
} }
(dir, env)
}
/// Benchmark of iterator sequential read performance.
#[bench]
fn bench_seq_iter(b: &mut Bencher) {
let n = 100;
let (_dir, env) = get_bench_ctx(n);
let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
b.iter(|| { b.iter(|| {
let mut cursor = txn.open_read_cursor(db).unwrap();
let mut i = 0; let mut i = 0;
for (key, data) in txn.iter(db).unwrap() { let mut count = 0u32;
for (key, data) in cursor.iter() {
i = i + key.len() + data.len(); i = i + key.len() + data.len();
count = count + 1;
} }
black_box(i); black_box(i);
assert_eq!(count, n);
}); });
} }
/// Benchmark of cursor sequential read performance.
#[bench] #[bench]
fn bench_cursor(b: &mut Bencher) { fn bench_seq_cursor(b: &mut Bencher) {
let n = 100u32; let n = 100;
let dir = io::TempDir::new("test").unwrap(); let (_dir, env) = get_bench_ctx(n);
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
{
let mut txn = env.begin_write_txn().unwrap();
for i in range(0, n) {
txn.put(db, format!("key{}", i).as_bytes(), format!("val{}", i).as_bytes(), WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
}
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
b.iter(|| { b.iter(|| {
let cursor = txn.open_read_cursor(db).unwrap(); let cursor = txn.open_read_cursor(db).unwrap();
let mut i = 0; let mut i = 0;
let mut count = 0u32;
let (key_opt, val) = cursor.get(None, None, MDB_FIRST).unwrap(); while let Ok((key_opt, val)) = cursor.get(None, None, MDB_NEXT) {
i = i + key_opt.map(|key| key.len()).unwrap_or(0) + val.len(); i += key_opt.map(|key| key.len()).unwrap_or(0) + val.len();
count += 1;
for _ in range(1, n) {
let (key_opt, val) = cursor.get(None, None, MDB_NEXT).unwrap();
i = i + key_opt.map(|key| key.len()).unwrap_or(0) + val.len();
} }
black_box(i); black_box(i);
assert_eq!(count, n);
}); });
} }
/// Benchmark of raw LMDB sequential read performance (control).
#[bench] #[bench]
fn bench_raw(b: &mut Bencher) { fn bench_seq_raw(b: &mut Bencher) {
let n = 100u32; let n = 100;
let dir = io::TempDir::new("test").unwrap(); let (_dir, env) = get_bench_ctx(n);
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
{
let mut txn = env.begin_write_txn().unwrap();
for i in range(0, n) {
txn.put(db, format!("key{}", i).as_bytes(), format!("val{}", i).as_bytes(), WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
}
let dbi: MDB_dbi = db.dbi(); let dbi: MDB_dbi = db.dbi();
let _txn = env.begin_read_txn().unwrap(); let _txn = env.begin_read_txn().unwrap();
let txn = _txn.txn(); let txn = _txn.txn();
let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
let mut data = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut data = MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
let mut cursor: *mut MDB_cursor = ptr::null_mut(); let mut cursor: *mut MDB_cursor = ptr::null_mut();
b.iter(|| unsafe { b.iter(|| unsafe {
assert_eq!(mdb_cursor_open(txn, dbi, &mut cursor), 0); mdb_cursor_open(txn, dbi, &mut cursor);
let mut i = 0; let mut i = 0;
let mut count = 0u32;
while mdb_cursor_get(cursor, &mut key, &mut data, MDB_NEXT) == 0 { while mdb_cursor_get(cursor, &mut key, &mut data, MDB_NEXT) == 0 {
i = i + key.mv_size + data.mv_size; i += key.mv_size + data.mv_size;
count += 1;
}; };
black_box(i); black_box(i);
assert_eq!(count, n);
mdb_cursor_close(cursor); mdb_cursor_close(cursor);
}); });
} }

@ -3,7 +3,7 @@ use std::{mem, ptr, raw};
use std::kinds::marker; use std::kinds::marker;
use std::io::BufWriter; use std::io::BufWriter;
use cursor::{RoCursor, RwCursor, Items}; use cursor::{RoCursor, RwCursor};
use environment::Environment; use environment::Environment;
use database::Database; use database::Database;
use error::{LmdbResult, lmdb_result}; use error::{LmdbResult, lmdb_result};
@ -81,13 +81,6 @@ pub trait ReadTransaction<'env> : Transaction<'env> {
RoCursor::new(self, db) RoCursor::new(self, db)
} }
/// Open a new read-only cursor on the given database.
fn iter<'txn>(&'txn self, db: Database) -> LmdbResult<Items<'txn>> {
Items::new(self, db)
}
/// Gets the option flags for the given database in the transaction. /// Gets the option flags for the given database in the transaction.
fn db_flags(&self, db: Database) -> LmdbResult<DatabaseFlags> { fn db_flags(&self, db: Database) -> LmdbResult<DatabaseFlags> {
let mut flags: c_uint = 0; let mut flags: c_uint = 0;

Loading…
Cancel
Save