324 lines
8.9 KiB
Rust
324 lines
8.9 KiB
Rust
use indexmap::IndexMap;
|
|
|
|
struct Lookup {
|
|
generated: Map<i64, i64>,
|
|
reference: IndexMap<i64, i64>,
|
|
keys: Vec<i64>,
|
|
}
|
|
|
|
struct TextLookup {
|
|
generated: Map<String, i64>,
|
|
reference: IndexMap<String, i64>,
|
|
keys: Vec<String>,
|
|
}
|
|
|
|
fn text_lookup_generated(values: &[TextLookup]) -> Result<i64, Fault> {
|
|
values.first().map_or(Ok(0), |value| {
|
|
GENERATED_TEXT_LOOKUP(&value.generated, &value.keys)
|
|
})
|
|
}
|
|
|
|
fn text_lookup_reference(values: &[TextLookup]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
let mut total = 0i64;
|
|
for key in &value.keys {
|
|
total = total
|
|
.checked_add(value.reference.get(key.as_str()).copied().unwrap_or(0))
|
|
.ok_or_else(overflow)?;
|
|
}
|
|
Ok(total)
|
|
}
|
|
|
|
fn text_lookup_same_table(values: &[TextLookup]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
let mut total = 0i64;
|
|
for key in &value.keys {
|
|
total = total
|
|
.checked_add(value.generated.get(key.as_str()).copied().unwrap_or(0))
|
|
.ok_or_else(overflow)?;
|
|
}
|
|
Ok(total)
|
|
}
|
|
|
|
fn text_lookup_comparisons() {
|
|
let reference_first = match std::env::var("DEVER_BENCH_MAP_ORDER")
|
|
.as_deref()
|
|
.unwrap_or("dever-first")
|
|
{
|
|
"dever-first" => false,
|
|
"rust-first" => true,
|
|
order => panic!("unknown table construction order: {order}"),
|
|
};
|
|
for count in [512, 1024, 2048, 4096] {
|
|
let keys: Vec<_> = (0..count)
|
|
.map(|index| format!("lookup-key-{index}"))
|
|
.collect();
|
|
let (mut generated, mut reference) = if reference_first {
|
|
let reference = IndexMap::with_capacity(count);
|
|
(Map::with_capacity(count), reference)
|
|
} else {
|
|
(Map::with_capacity(count), IndexMap::with_capacity(count))
|
|
};
|
|
for key in &keys {
|
|
if reference_first {
|
|
reference.insert(key.clone(), 120);
|
|
generated.insert(key.clone(), 120);
|
|
} else {
|
|
generated.insert(key.clone(), 120);
|
|
reference.insert(key.clone(), 120);
|
|
}
|
|
}
|
|
assert_eq!(
|
|
GENERATED_TEXT_LOOKUP(&generated, &["missing".to_owned()]).unwrap(),
|
|
0
|
|
);
|
|
let values = [TextLookup {
|
|
generated,
|
|
reference,
|
|
keys,
|
|
}];
|
|
compare(
|
|
&format!("map_text_lookup_{count}"),
|
|
&values,
|
|
200,
|
|
text_lookup_generated,
|
|
text_lookup_reference,
|
|
|value| *value as u64,
|
|
);
|
|
// 同表只隔离生成层;独立 IndexMap 参照不能被这个控制组替代。
|
|
compare_named(
|
|
&format!("map_text_lookup_{count}_same_table"),
|
|
&values,
|
|
200,
|
|
text_lookup_generated,
|
|
text_lookup_same_table,
|
|
|value| *value as u64,
|
|
"rust_runtime",
|
|
);
|
|
}
|
|
}
|
|
|
|
fn overflow() -> Fault {
|
|
&FaultInfo {
|
|
message: "integer overflow",
|
|
source: "reference.dever",
|
|
line: 1,
|
|
column: 1,
|
|
}
|
|
}
|
|
|
|
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 answer = 0i64;
|
|
for key in &value.keys {
|
|
answer = answer
|
|
.checked_add(value.reference.get(key).copied().unwrap_or(0))
|
|
.ok_or_else(overflow)?;
|
|
}
|
|
Ok(answer)
|
|
}
|
|
|
|
fn lookup_same_table(values: &[Lookup]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
let mut answer = 0i64;
|
|
for key in &value.keys {
|
|
answer = answer
|
|
.checked_add(value.generated.get(key).copied().unwrap_or(0))
|
|
.ok_or_else(overflow)?;
|
|
}
|
|
Ok(answer)
|
|
}
|
|
|
|
fn scan_generated(values: &[Lookup]) -> Result<i64, Fault> {
|
|
match values.first() {
|
|
Some(value) => GENERATED_SCAN(&value.generated),
|
|
None => Ok(0),
|
|
}
|
|
}
|
|
|
|
fn scan_reference(values: &[Lookup]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(0);
|
|
};
|
|
value.reference.values().try_fold(0i64, |total, item| {
|
|
total.checked_add(*item).ok_or_else(overflow)
|
|
})
|
|
}
|
|
|
|
fn map_generated(values: &[i64]) -> Result<Vec<(i64, i64)>, Fault> {
|
|
GENERATED_ENTRIES(values)
|
|
}
|
|
|
|
fn map_reference(values: &[i64]) -> Result<Vec<(i64, i64)>, Fault> {
|
|
let mut result = IndexMap::new();
|
|
for value in values {
|
|
result.insert(*value, *value);
|
|
}
|
|
Ok(result.into_iter().collect())
|
|
}
|
|
|
|
fn list_reference(values: &[i64]) -> Result<Vec<i64>, Fault> {
|
|
let mut result = Vec::new();
|
|
for value in values {
|
|
result.push(*value);
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn buffer_reference(values: &dever_runtime::Bytes) -> Result<BUFFER_STATE, Fault> {
|
|
let mut items = Vec::new();
|
|
let mut count = 0i64;
|
|
for value in values.iter() {
|
|
items.push(i64::from(*value));
|
|
count = count.checked_add(1).ok_or_else(overflow)?;
|
|
}
|
|
Ok(BUFFER_STATE {
|
|
field0: items,
|
|
field1: count,
|
|
})
|
|
}
|
|
|
|
fn buffer_inspect_reference(values: &dever_runtime::Bytes) -> Result<BUFFER_STATE, Fault> {
|
|
let mut items = Vec::new();
|
|
let mut count = 0i64;
|
|
for value in values.iter() {
|
|
count = (items.len() as i64).checked_add(1).ok_or_else(overflow)?;
|
|
items.push(i64::from(*value));
|
|
}
|
|
Ok(BUFFER_STATE {
|
|
field0: items,
|
|
field1: count,
|
|
})
|
|
}
|
|
|
|
fn read_records_reference(values: &[SCALAR_ROW]) -> Result<i64, Fault> {
|
|
let mut total = 0i64;
|
|
for row in values {
|
|
let projected = row
|
|
.field0
|
|
.checked_add(row.field1)
|
|
.ok_or_else(overflow)?
|
|
.checked_add(row.field2)
|
|
.ok_or_else(overflow)?;
|
|
total = total.checked_add(projected).ok_or_else(overflow)?;
|
|
}
|
|
Ok(total)
|
|
}
|
|
|
|
fn main() {
|
|
let values: Vec<i64> = (0..1024).collect();
|
|
let lookup = Lookup {
|
|
generated: GENERATED_BUILD_MAP(&values).unwrap(),
|
|
reference: values.iter().map(|value| (*value, *value)).collect(),
|
|
keys: (0..1024).map(|index| (index * 17) % 1152).collect(),
|
|
};
|
|
let lookups = [lookup];
|
|
let overflow_case = [Lookup {
|
|
generated: GENERATED_BUILD_MAP(&[i64::MAX, 1]).unwrap(),
|
|
reference: [(i64::MAX, i64::MAX), (1, 1)].into_iter().collect(),
|
|
keys: vec![i64::MAX, 1],
|
|
}];
|
|
assert!(scan_generated(&overflow_case).is_err());
|
|
assert!(scan_reference(&overflow_case).is_err());
|
|
assert!(lookup_generated(&overflow_case).is_err());
|
|
assert!(lookup_reference(&overflow_case).is_err());
|
|
assert_eq!(
|
|
map_generated(&[3, 1, 3, 2]).unwrap(),
|
|
map_reference(&[3, 1, 3, 2]).unwrap()
|
|
);
|
|
println!("case,sample,implementation,elapsed_ns,checksum");
|
|
text_lookup_comparisons();
|
|
for count in [512, 1024, 2048, 4096] {
|
|
let bytes = dever_runtime::Bytes::from(vec![120u8; count]);
|
|
let rows: Vec<_> = bytes
|
|
.iter()
|
|
.map(|value| SCALAR_ROW {
|
|
field0: i64::from(*value),
|
|
field1: 0,
|
|
field2: 0,
|
|
})
|
|
.collect();
|
|
compare(
|
|
&format!("list_record_read_{count}"),
|
|
&rows,
|
|
1000,
|
|
GENERATED_RECORD_READ,
|
|
read_records_reference,
|
|
|value| *value as u64,
|
|
);
|
|
compare_bytes(
|
|
&format!("record_update_{count}"),
|
|
&bytes,
|
|
1000,
|
|
GENERATED_BUFFER_BUILD,
|
|
buffer_reference,
|
|
|value| value.field1 as u64,
|
|
);
|
|
compare_bytes(
|
|
&format!("record_inspect_{count}"),
|
|
&bytes,
|
|
1000,
|
|
GENERATED_BUFFER_INSPECT,
|
|
buffer_inspect_reference,
|
|
|value| value.field1 as u64,
|
|
);
|
|
}
|
|
compare(
|
|
"map_lookup",
|
|
&lookups,
|
|
2000,
|
|
lookup_generated,
|
|
lookup_reference,
|
|
|value| *value as u64,
|
|
);
|
|
// 同一张表隔离随机哈希布局;独立 IndexMap 参照仍保留。
|
|
compare_named(
|
|
"map_lookup_same_table",
|
|
&lookups,
|
|
2000,
|
|
lookup_generated,
|
|
lookup_same_table,
|
|
|value| *value as u64,
|
|
"rust_runtime",
|
|
);
|
|
compare(
|
|
"map_scan",
|
|
&lookups,
|
|
20000,
|
|
scan_generated,
|
|
scan_reference,
|
|
|value| *value as u64,
|
|
);
|
|
compare(
|
|
"map_build",
|
|
&values[..512],
|
|
400,
|
|
map_generated,
|
|
map_reference,
|
|
|value| value.len() as u64,
|
|
);
|
|
compare(
|
|
"list_build",
|
|
&values,
|
|
1000,
|
|
GENERATED_BUILD_LIST,
|
|
list_reference,
|
|
|value| value.len() as u64,
|
|
);
|
|
}
|