203 lines
5.3 KiB
Rust
203 lines
5.3 KiB
Rust
use indexmap::IndexMap;
|
|
|
|
struct Lookup {
|
|
generated: Map<Id, i64>,
|
|
reference: IndexMap<Id, i64>,
|
|
keys: Vec<Id>,
|
|
}
|
|
|
|
struct Comparison {
|
|
values: Vec<Id>,
|
|
expected: Id,
|
|
}
|
|
|
|
fn id_fault() -> Fault {
|
|
&FaultInfo {
|
|
message: "integer arithmetic fault",
|
|
source: "reference.dever",
|
|
line: 1,
|
|
column: 1,
|
|
}
|
|
}
|
|
|
|
fn roundtrip_generated(values: &[String]) -> Result<String, Fault> {
|
|
GENERATED_ROUNDTRIP(values.first().map(String::as_str).unwrap_or(""))
|
|
}
|
|
|
|
fn roundtrip_reference(values: &[String]) -> Result<String, Fault> {
|
|
let id = Id::from(values.first().map(String::as_str).unwrap_or("").to_owned());
|
|
Ok(id.into_text())
|
|
}
|
|
|
|
fn encode_reference(values: &[String]) -> Result<Vec<Id>, Fault> {
|
|
Ok(values.iter().map(|text| Id::from(text.clone())).collect())
|
|
}
|
|
|
|
fn compare_generated(values: &[Comparison]) -> Result<i64, Fault> {
|
|
match values.first() {
|
|
Some(value) => GENERATED_COMPARE(&value.values, &value.expected),
|
|
None => Ok(0),
|
|
}
|
|
}
|
|
|
|
fn compare_reference(values: &[Comparison]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
let expected = value.expected.clone();
|
|
let mut count = 0i64;
|
|
for item in &value.values {
|
|
count = count
|
|
.checked_add(i64::from(item == &expected))
|
|
.ok_or_else(id_fault)?;
|
|
}
|
|
Ok(count)
|
|
}
|
|
|
|
fn lookup_generated(values: &[Lookup]) -> Result<i64, Fault> {
|
|
match values.first() {
|
|
Some(value) => GENERATED_LOOKUP(&value.generated, &value.keys),
|
|
None => Ok(0),
|
|
}
|
|
}
|
|
|
|
fn lookup_reference(values: &[Lookup]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
let mut count = 0i64;
|
|
for key in &value.keys {
|
|
count = count
|
|
.checked_add(value.reference.get(key).copied().unwrap_or(0))
|
|
.ok_or_else(id_fault)?;
|
|
}
|
|
Ok(count)
|
|
}
|
|
|
|
fn lookup_same_table(values: &[Lookup]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
let mut count = 0i64;
|
|
for key in &value.keys {
|
|
count = count
|
|
.checked_add(value.generated.get(key).copied().unwrap_or(0))
|
|
.ok_or_else(id_fault)?;
|
|
}
|
|
Ok(count)
|
|
}
|
|
|
|
fn map_reference(values: &[String]) -> Result<Vec<(Id, i64)>, Fault> {
|
|
let mut map = IndexMap::new();
|
|
for text in values {
|
|
map.insert(Id::from(text.clone()), text.chars().count() as i64);
|
|
}
|
|
Ok(map.into_iter().collect())
|
|
}
|
|
|
|
fn main() {
|
|
let texts: Vec<String> = (0..512)
|
|
.map(|index| format!("user-{index:04}-雪"))
|
|
.collect();
|
|
let short = ["user-0012-雪".to_owned()];
|
|
let long = ["雪山树林".repeat(512)];
|
|
let reference = || {
|
|
texts
|
|
.iter()
|
|
.map(|text| (Id::from(text.clone()), text.chars().count() as i64))
|
|
.collect()
|
|
};
|
|
// 建表顺序影响独立表的哈希与分配布局;复测须等量覆盖两种顺序。
|
|
let (generated, reference) = match std::env::var("DEVER_BENCH_MAP_ORDER")
|
|
.as_deref()
|
|
.unwrap_or("dever-first")
|
|
{
|
|
"dever-first" => {
|
|
let generated = GENERATED_BUILD_MAP(&texts).unwrap();
|
|
(generated, reference())
|
|
}
|
|
"rust-first" => {
|
|
let reference = reference();
|
|
(GENERATED_BUILD_MAP(&texts).unwrap(), reference)
|
|
}
|
|
order => panic!("unknown table construction order: {order}"),
|
|
};
|
|
let keys = (0..1024)
|
|
.map(|index| Id::from(format!("user-{:04}-雪", (index * 17) % 576)))
|
|
.collect();
|
|
let lookups = [Lookup {
|
|
generated,
|
|
reference,
|
|
keys,
|
|
}];
|
|
let comparisons = [Comparison {
|
|
values: (0..1024)
|
|
.map(|index| Id::from(format!("user-{:04}-雪", index % 32)))
|
|
.collect(),
|
|
expected: Id::from("user-0012-雪".to_owned()),
|
|
}];
|
|
assert_eq!(
|
|
GENERATED_ENTRIES(&["a".into(), "b".into(), "a".into()]).unwrap(),
|
|
map_reference(&["a".into(), "b".into(), "a".into()]).unwrap()
|
|
);
|
|
println!("case,sample,implementation,elapsed_ns,checksum");
|
|
compare(
|
|
"id_roundtrip_short",
|
|
&short,
|
|
300000,
|
|
roundtrip_generated,
|
|
roundtrip_reference,
|
|
|value| value.len() as u64,
|
|
);
|
|
compare(
|
|
"id_roundtrip_long",
|
|
&long,
|
|
20000,
|
|
roundtrip_generated,
|
|
roundtrip_reference,
|
|
|value| value.len() as u64,
|
|
);
|
|
compare(
|
|
"id_each",
|
|
&texts,
|
|
300,
|
|
GENERATED_ENCODE_ALL,
|
|
encode_reference,
|
|
|value| value.len() as u64,
|
|
);
|
|
compare(
|
|
"id_compare",
|
|
&comparisons,
|
|
2000,
|
|
compare_generated,
|
|
compare_reference,
|
|
|value| *value as u64,
|
|
);
|
|
compare(
|
|
"id_map_lookup",
|
|
&lookups,
|
|
1000,
|
|
lookup_generated,
|
|
lookup_reference,
|
|
|value| *value as u64,
|
|
);
|
|
// 双方读取同一张表,避免将各自随机哈希状态的差异归给生成代码。
|
|
compare_named(
|
|
"id_map_lookup_same_table",
|
|
&lookups,
|
|
1000,
|
|
lookup_generated,
|
|
lookup_same_table,
|
|
|value| *value as u64,
|
|
"rust_runtime",
|
|
);
|
|
compare(
|
|
"id_map_build",
|
|
&texts,
|
|
300,
|
|
GENERATED_ENTRIES,
|
|
map_reference,
|
|
|value| value.len() as u64,
|
|
);
|
|
}
|