main.rs
148 lines
| 4.3 KiB
| application/rls-services+xml
|
RustLexer
Georges Racinet
|
r44870 | // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net> | ||
// | ||||
// This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | ||||
use hg::revlog::node::*; | ||||
use hg::revlog::nodemap::*; | ||||
use hg::revlog::*; | ||||
Simon Sapin
|
r48767 | use memmap2::MmapOptions; | ||
Georges Racinet
|
r44870 | use rand::Rng; | ||
use std::fs::File; | ||||
use std::io; | ||||
use std::io::Write; | ||||
use std::path::{Path, PathBuf}; | ||||
use std::time::Instant; | ||||
mod index; | ||||
use index::Index; | ||||
fn mmap_index(repo_path: &Path) -> Index { | ||||
let mut path = PathBuf::from(repo_path); | ||||
path.extend([".hg", "store", "00changelog.i"].iter()); | ||||
Index::load_mmap(path) | ||||
} | ||||
fn mmap_nodemap(path: &Path) -> NodeTree { | ||||
let file = File::open(path).unwrap(); | ||||
let mmap = unsafe { MmapOptions::new().map(&file).unwrap() }; | ||||
let len = mmap.len(); | ||||
NodeTree::load_bytes(Box::new(mmap), len) | ||||
} | ||||
/// Scan the whole index and create the corresponding nodemap file at `path` | ||||
fn create(index: &Index, path: &Path) -> io::Result<()> { | ||||
let mut file = File::create(path)?; | ||||
let start = Instant::now(); | ||||
let mut nm = NodeTree::default(); | ||||
for rev in 0..index.len() { | ||||
Raphaël Gomès
|
r51872 | let rev = Revision(rev as BaseRevision); | ||
Georges Racinet
|
r44870 | nm.insert(index, index.node(rev).unwrap(), rev).unwrap(); | ||
} | ||||
eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed()); | ||||
Raphaël Gomès
|
r50815 | file.write_all(&nm.into_readonly_and_added_bytes().1)?; | ||
Georges Racinet
|
r44870 | eprintln!("Nodemap written to disk"); | ||
Ok(()) | ||||
} | ||||
fn query(index: &Index, nm: &NodeTree, prefix: &str) { | ||||
let start = Instant::now(); | ||||
Simon Sapin
|
r47161 | let res = NodePrefix::from_hex(prefix).map(|p| nm.find_bin(index, p)); | ||
Georges Racinet
|
r44870 | println!("Result found in {:?}: {:?}", start.elapsed(), res); | ||
} | ||||
fn bench(index: &Index, nm: &NodeTree, queries: usize) { | ||||
let len = index.len() as u32; | ||||
let mut rng = rand::thread_rng(); | ||||
let nodes: Vec<Node> = (0..queries) | ||||
Raphaël Gomès
|
r51872 | .map(|_| { | ||
*index | ||||
.node(Revision((rng.gen::<u32>() % len) as BaseRevision)) | ||||
.unwrap() | ||||
}) | ||||
Georges Racinet
|
r44870 | .collect(); | ||
if queries < 10 { | ||||
let nodes_hex: Vec<String> = | ||||
Simon Sapin
|
r47155 | nodes.iter().map(|n| format!("{:x}", n)).collect(); | ||
Georges Racinet
|
r44870 | println!("Nodes: {:?}", nodes_hex); | ||
} | ||||
let mut last: Option<Revision> = None; | ||||
let start = Instant::now(); | ||||
for node in nodes.iter() { | ||||
last = nm.find_bin(index, node.into()).unwrap(); | ||||
} | ||||
let elapsed = start.elapsed(); | ||||
println!( | ||||
Simon Sapin
|
r47155 | "Did {} queries in {:?} (mean {:?}), last was {:x} with result {:?}", | ||
Georges Racinet
|
r44870 | queries, | ||
elapsed, | ||||
elapsed / (queries as u32), | ||||
Simon Sapin
|
r47155 | nodes.last().unwrap(), | ||
Georges Racinet
|
r44870 | last | ||
); | ||||
} | ||||
fn main() { | ||||
Raphaël Gomès
|
r50531 | use clap::{Parser, Subcommand}; | ||
Georges Racinet
|
r44870 | |||
Raphaël Gomès
|
r50531 | #[derive(Parser)] | ||
#[command()] | ||||
/// Nodemap pure Rust example | ||||
struct App { | ||||
// Path to the repository, always necessary for its index | ||||
#[arg(short, long)] | ||||
repository: PathBuf, | ||||
// Path to the nodemap file, independent of REPOSITORY | ||||
#[arg(short, long)] | ||||
nodemap_file: PathBuf, | ||||
#[command(subcommand)] | ||||
command: Command, | ||||
} | ||||
Georges Racinet
|
r44870 | |||
Raphaël Gomès
|
r50531 | #[derive(Subcommand)] | ||
enum Command { | ||||
/// Create `NODEMAP_FILE` by scanning repository index | ||||
Create, | ||||
/// Query `NODEMAP_FILE` for `prefix` | ||||
Query { prefix: String }, | ||||
/// Perform #`QUERIES` random successful queries on `NODEMAP_FILE` | ||||
Bench { queries: usize }, | ||||
Georges Racinet
|
r44870 | } | ||
Raphaël Gomès
|
r50531 | let app = App::parse(); | ||
let repo = &app.repository; | ||||
let nm_path = &app.nodemap_file; | ||||
let index = mmap_index(repo); | ||||
let nm = mmap_nodemap(nm_path); | ||||
match &app.command { | ||||
Command::Create => { | ||||
println!( | ||||
"Creating nodemap file {} for repository {}", | ||||
nm_path.display(), | ||||
repo.display() | ||||
); | ||||
Raphaël Gomès
|
r50809 | create(&index, Path::new(nm_path)).unwrap(); | ||
Raphaël Gomès
|
r50531 | } | ||
Command::Bench { queries } => { | ||||
println!( | ||||
"Doing {} random queries in nodemap file {} of repository {}", | ||||
queries, | ||||
nm_path.display(), | ||||
repo.display() | ||||
); | ||||
bench(&index, &nm, *queries); | ||||
} | ||||
Command::Query { prefix } => { | ||||
println!( | ||||
"Querying {} in nodemap file {} of repository {}", | ||||
prefix, | ||||
nm_path.display(), | ||||
repo.display() | ||||
); | ||||
query(&index, &nm, prefix); | ||||
} | ||||
Georges Racinet
|
r44870 | } | ||
} | ||||