use indexmap::IndexMap; use std::hint::black_box; use std::time::Instant; struct Record { first: i64, second: i64, third: i64, } struct OwnedRecord { name: String, tags: Vec, count: i64, } fn inspect_owned(row: &OwnedRecord) -> i64 { add( add(row.count, row.name.chars().count() as i64), row.tags.len() as i64, ) } fn forward_owned(row: &OwnedRecord) -> i64 { inspect_owned(row) } fn add(total: i64, value: i64) -> i64 { total.checked_add(value).expect("integer overflow") } fn record_value(row: &Record) -> i64 { add(add(row.first, row.second), row.third) } fn measure(operation: &str, ticks: &str) -> serde_json::Value { let source = black_box(ticks.as_bytes()); let (checksum, elapsed) = match operation { "record_update" => { let begin = Instant::now(); let mut values = Vec::new(); let mut count = 0; for value in source { values.push(i64::from(*value)); count = add(count, 1); } black_box((&values, count)); let elapsed = begin.elapsed(); let total = values.iter().fold(0, |total, value| add(total, *value)); let checksum = add(total, count) .checked_sub(values.len() as i64) .expect("integer overflow"); (checksum, elapsed) } "record_inspect" => { let begin = Instant::now(); let mut values = Vec::new(); let mut count = 0; for value in source { count = add(values.len() as i64, 1); values.push(i64::from(*value)); } black_box((&values, count)); let elapsed = begin.elapsed(); let total = values.iter().fold(0, |total, value| add(total, *value)); let checksum = add(total, count) .checked_sub(values.len() as i64) .expect("integer overflow"); (checksum, elapsed) } "map_lookup" => { let values = source .iter() .enumerate() .map(|(index, value)| (index as i64, i64::from(*value))) .collect::>(); let borrowed = black_box(&values); let mut index = 0; let mut total = 0; let begin = Instant::now(); for _ in source { total = add(total, borrowed.get(&index).copied().unwrap_or(0)); index = add(index, 1); } black_box(total); let elapsed = begin.elapsed(); black_box(&values); (total, elapsed) } "text_equal" | "text_contains" => { let rows = source .iter() .map(|value| { let left = format!("compare-value-{value}"); let right = left.to_uppercase().to_lowercase(); (left, right) }) .collect::>(); let borrowed = black_box(&rows); let mut total = 0; let begin = Instant::now(); if operation == "text_equal" { for (left, right) in borrowed { total = add(total, if left == right { 120 } else { 0 }); } } else { for (left, right) in borrowed { total = add( total, if left.contains(right.as_str()) { 120 } else { 0 }, ); } } black_box(total); let elapsed = begin.elapsed(); black_box(&rows); let checksum = add(total, rows.len() as i64) .checked_sub(source.len() as i64) .expect("integer overflow"); (checksum, elapsed) } "map_text_lookup" => { // 与 Dever 使用相同的拥有键和插入顺序;HashMap<&str, _> 是不同的数据结构。 let values = source .iter() .enumerate() .map(|(index, value)| (format!("lookup-key-{index}"), i64::from(*value))) .collect::>(); let keys = values.keys().cloned().collect::>(); let borrowed = black_box(&values); let mut total = 0; let begin = Instant::now(); for key in &keys { total = add(total, borrowed.get(key.as_str()).copied().unwrap_or(0)); } black_box(total); let elapsed = begin.elapsed(); black_box((&keys, &values)); let checksum = add(total, keys.len() as i64) .checked_sub(source.len() as i64) .expect("integer overflow"); (checksum, elapsed) } "call_read" => { let rows = source .iter() .map(|value| { let name = format!("row-value-{value}"); OwnedRecord { count: i64::from(*value) - name.chars().count() as i64 - 1, tags: vec![name.clone()], name, } }) .collect::>(); let borrowed = black_box(&rows); let begin = Instant::now(); let total = borrowed .iter() .fold(0, |total, row| add(total, forward_owned(row))); black_box(total); let elapsed = begin.elapsed(); black_box(&rows); (total, elapsed) } "float" => { let begin = Instant::now(); let total = source .iter() .fold(0.0, |total, value| total + f64::from(*value)); black_box(total); let elapsed = begin.elapsed(); (total as i64, elapsed) } "bytes_concat" => { let chunks = source.chunks(1).map(<[u8]>::to_vec).collect::>(); let begin = Instant::now(); let mut output = Vec::new(); for chunk in &chunks { output.extend_from_slice(chunk); } black_box(&output); let elapsed = begin.elapsed(); drop(chunks); let total = output .iter() .fold(0, |total, byte| add(total, i64::from(*byte))); (total, elapsed) } "text_concat" => { let chunks = std::str::from_utf8(source) .unwrap() .split(',') .map(str::to_owned) .collect::>(); let begin = Instant::now(); let mut output = String::new(); for chunk in &chunks { output.push_str(chunk); } black_box(&output); let elapsed = begin.elapsed(); drop(chunks); let total = output .bytes() .fold(0, |total, byte| add(total, i64::from(byte))); (total, elapsed) } "integer" => { let begin = Instant::now(); let total = source .iter() .fold(0, |total, value| add(total, i64::from(*value))); (black_box(total), begin.elapsed()) } "list" | "list_read" => { let mut values = Vec::new(); let begin = Instant::now(); values.extend(source.iter().map(|value| i64::from(*value))); let construction = begin.elapsed(); let borrowed = black_box(&values); let begin = Instant::now(); let total = borrowed.iter().fold(0, |total, value| add(total, *value)); black_box(total); let traversal = begin.elapsed(); drop(values); ( total, if operation == "list" { construction } else { traversal }, ) } "list_record" | "list_record_read" => { let mut values = Vec::new(); let begin = Instant::now(); for value in source { values.push(Record { first: i64::from(*value), second: 0, third: 0, }); } let construction = begin.elapsed(); let borrowed = black_box(&values); let begin = Instant::now(); let total = borrowed .iter() .fold(0, |total, row| add(total, record_value(row))); black_box(total); let traversal = begin.elapsed(); drop(values); ( total, if operation == "list_record" { construction } else { traversal }, ) } "map" | "map_read" => { let mut values = IndexMap::new(); let begin = Instant::now(); for value in source { let key = i64::try_from(values.len()).expect("map size"); values.insert(key, i64::from(*value)); } let construction = begin.elapsed(); let borrowed = black_box(&values); let begin = Instant::now(); let total = borrowed.values().fold(0, |total, value| add(total, *value)); black_box(total); drop(values); let traversal = begin.elapsed(); ( total, if operation == "map" { construction } else { traversal }, ) } _ => panic!("unknown kernel"), }; serde_json::json!({"checksum": checksum, "elapsed_ns": elapsed.as_nanos()}) } fn main() { let arguments = std::env::args().collect::>(); let input: serde_json::Value = serde_json::from_str(&arguments[2]).expect("input object"); let ticks = input["ticks"].as_str().expect("ticks string"); let output = if let Some(operation) = arguments[1].strip_prefix("continuous_") { serde_json::Value::Array((0..10).map(|_| measure(operation, ticks)).collect()) } else { measure(&arguments[1], ticks) }; println!("{output}"); }