Files
core/test/rust/performance/numeric/operations.rs
T
2026-11-03 03:32:07 +00:00

310 lines
10 KiB
Rust

// 算术参照直接调用 dec,逐步检查同样的错误;不降低精度或重排运算。
use dec::{Context, Decimal128};
const TWO: Decimal128 = Decimal128::from_ne_bytes(
u128::from_be_bytes([34, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]).to_ne_bytes(),
);
// 两套表示在计时前准备,Rust 循环不经过 Dever 的输入/输出转换。
struct Inputs<Generated, Native> {
generated: Vec<Generated>,
native: Vec<Native>,
}
fn inputs<Generated: Copy, Native>(
values: &[Generated],
convert: impl FnMut(Generated) -> Native,
) -> [Inputs<Generated, Native>; 1] {
[Inputs {
generated: values.to_vec(),
native: values.iter().copied().map(convert).collect(),
}]
}
fn native_bytes(value: Decimal128) -> [u8; 16] {
u128::from_ne_bytes(value.to_ne_bytes()).to_be_bytes()
}
fn raw(value: Decimal) -> Decimal128 {
Decimal128::from_be_bytes(value.to_be_bytes())
}
fn checked(value: Decimal128, context: &Context<Decimal128>) -> Result<Decimal128, Fault> {
if value.is_finite() && !context.status().overflow() && !context.status().invalid_operation() {
Ok(value)
} else {
Err(&FaultInfo {
message: "Decimal arithmetic fault",
source: "Rust reference",
line: 0,
column: 0,
})
}
}
fn operation<const KIND: usize>(
context: &mut Context<Decimal128>,
left: Decimal128,
right: Decimal128,
) -> Result<Decimal128, Fault> {
context.clear_status();
let value = match KIND {
0 => context.add(left, right),
1 => context.sub(left, right),
2 => context.mul(left, right),
3 => context.div(left, right),
4 => {
let product = operation::<2>(context, left, right)?;
let added = operation::<0>(context, product, left)?;
let divided = operation::<3>(context, added, right)?;
return operation::<1>(context, divided, left);
}
5 => context.mul(left, TWO),
_ => unreachable!(),
};
checked(value, context)
}
fn generated_one<const KIND: usize>(left: Decimal, right: Decimal) -> Result<Decimal, Fault> {
match KIND {
0 => GENERATED_ADD(left, right),
1 => GENERATED_SUBTRACT(left, right),
2 => GENERATED_MULTIPLY(left, right),
3 => GENERATED_DIVIDE(left, right),
4 => GENERATED_CALCULATE(left, right),
5 => GENERATED_SCALE(left),
_ => unreachable!(),
}
}
fn score(bytes: [u8; 16]) -> u64 {
u64::from_be_bytes(bytes[8..].try_into().unwrap())
}
fn generated<const KIND: usize>(
inputs: &[Inputs<(Decimal, Decimal), (Decimal128, Decimal128)>],
) -> Result<u64, Fault> {
let values = inputs.first().map_or(&[][..], |input| input.generated.as_slice());
let mut total = 0u64;
for (left, right) in values {
total = total.wrapping_add(score(generated_one::<KIND>(*left, *right)?.to_be_bytes()));
}
Ok(total)
}
fn reference<const KIND: usize>(
inputs: &[Inputs<(Decimal, Decimal), (Decimal128, Decimal128)>],
) -> Result<u64, Fault> {
let values = inputs.first().map_or(&[][..], |input| input.native.as_slice());
let mut context = Context::<Decimal128>::default();
let mut total = 0u64;
for (left, right) in values {
let value = operation::<KIND>(&mut context, *left, *right)?;
total = total.wrapping_add(score(native_bytes(value)));
}
Ok(total)
}
fn compare_operation<const KIND: usize>(name: &str, values: &[(Decimal, Decimal)]) {
let mut context = Context::<Decimal128>::default();
let maximum = Decimal::parse("9.999999999999999999999999999999999E+6144").unwrap();
for (left, right) in values
.iter()
.copied()
.chain([(maximum, maximum), (maximum, Decimal::from_int(0))])
{
let actual = generated_one::<KIND>(left, right);
let expected = operation::<KIND>(&mut context, raw(left), raw(right));
assert_eq!(actual.is_err(), expected.is_err());
if let (Ok(actual), Ok(expected)) = (actual, expected) {
assert_eq!(actual.to_be_bytes(), expected.to_be_bytes());
}
}
let inputs = inputs(values, |(left, right)| (raw(left), raw(right)));
compare(
name,
&inputs,
1000,
generated::<KIND>,
reference::<KIND>,
|value| *value,
);
}
fn projected(inputs: &[Inputs<Decimal, Decimal128>]) -> Result<[u8; 16], Fault> {
let values = inputs.first().map_or(&[][..], |input| input.generated.as_slice());
GENERATED_SCALED(values).map(Decimal::to_be_bytes)
}
fn projected_reference(inputs: &[Inputs<Decimal, Decimal128>]) -> Result<[u8; 16], Fault> {
let values = inputs.first().map_or(&[][..], |input| input.native.as_slice());
let mut context = Context::<Decimal128>::default();
let mut total = Decimal128::ZERO;
for value in values {
let next = operation::<5>(&mut context, *value, Decimal128::ZERO)?;
total = operation::<0>(&mut context, total, next)?;
}
Ok(native_bytes(total))
}
fn converted(values: &[i64]) -> Result<u64, Fault> {
let mut total = 0u64;
for value in values {
total = total.wrapping_add(score(GENERATED_CONVERT(*value)?.to_be_bytes()));
}
Ok(total)
}
fn converted_reference(values: &[i64]) -> Result<u64, Fault> {
let mut total = 0u64;
for value in values {
let decimal = if let Ok(narrow) = i32::try_from(*value) {
Decimal128::from(narrow)
} else if let Ok(narrow) = u32::try_from(*value) {
Decimal128::from(narrow)
} else {
Decimal128::from(*value)
};
total = total.wrapping_add(score(native_bytes(decimal)));
}
Ok(total)
}
fn converted_direct_reference(values: &[i64]) -> Result<u64, Fault> {
let mut total = 0u64;
for value in values {
total = total.wrapping_add(score(native_bytes(Decimal128::from(*value))));
}
Ok(total)
}
fn main() {
let constants = GENERATED_CONSTANTS().unwrap();
let expected = [
Decimal128::ZERO,
Context::<Decimal128>::default().parse("-0.00").unwrap(),
Decimal128::from(i64::MIN),
Decimal128::from(i64::MAX),
Decimal128::from(3i64),
];
assert_eq!(constants.len(), expected.len());
for (actual, expected) in constants.iter().zip(expected) {
assert_eq!(actual.to_be_bytes(), expected.to_be_bytes());
}
let left = [
"-0.00",
"1.25",
"-7.5",
"1E-60",
"9999999999999999999999999999999999",
"-123.456",
"0.9999999999999999999999999999999999",
"1234567.890",
]
.map(|text| Decimal::parse(text).unwrap());
let right = [
"3.25",
"-2",
"0.1",
"1.000000000000000000000000000000001",
"2",
"10",
"-3",
"1",
]
.map(|text| Decimal::parse(text).unwrap());
let values = (0..256)
.map(|index| (left[index % left.len()], right[index % right.len()]))
.collect::<Vec<_>>();
println!("case,sample,implementation,elapsed_ns,checksum");
compare_operation::<0>("decimal_add", &values);
compare_operation::<1>("decimal_subtract", &values);
compare_operation::<2>("decimal_multiply", &values);
compare_operation::<3>("decimal_divide", &values);
compare_operation::<4>("decimal_calculate", &values);
compare_operation::<5>("decimal_literal_scale", &values);
let items = values.iter().map(|(left, _)| *left).collect::<Vec<_>>();
let prepared = inputs(&items, raw);
assert_eq!(projected(&prepared).unwrap(), projected_reference(&prepared).unwrap());
let large = Decimal::parse("4E+6144").unwrap();
let maximum = Decimal::parse("9.999999999999999999999999999999999E+6144").unwrap();
for items in [vec![], vec![large, large, maximum], vec![maximum, large]] {
let prepared = inputs(&items, raw);
let actual = projected(&prepared);
let expected = projected_reference(&prepared);
assert_eq!(actual.is_err(), expected.is_err());
match (actual, expected) {
(Ok(actual), Ok(expected)) => assert_eq!(actual, expected),
(Err(actual), Err(_)) => {
let source = std::fs::read_to_string(actual.source).unwrap();
let needle = if items[0] == maximum {
"value * 2"
} else {
"sum(scale, values)"
};
assert_eq!(
actual.line,
source[..source.find(needle).unwrap()]
.bytes()
.filter(|byte| *byte == b'\n')
.count()
+ 1
);
}
_ => unreachable!(),
}
}
compare(
"decimal_projected_sum",
&prepared,
1000,
projected,
projected_reference,
|value| score(*value),
);
let numbers = [
i64::MIN,
i64::MAX,
-1,
0,
1,
i32::MIN as i64,
i32::MAX as i64,
1234567890123456789,
];
for value in numbers {
assert_eq!(
GENERATED_CONVERT(value).unwrap().to_be_bytes(),
Decimal128::from(value).to_be_bytes()
);
}
let integers = (0..256)
.map(|index| numbers[index % numbers.len()])
.collect::<Vec<_>>();
compare(
"decimal_int_conversion",
&integers,
1000,
converted,
converted_reference,
|value| *value,
);
for (name, numbers) in [
("decimal_int_conversion_narrow", [
i64::from(i32::MIN), -1, 0, 1, i64::from(i32::MAX),
i64::from(i32::MAX) + 1, i64::from(u32::MAX) - 1, i64::from(u32::MAX),
]),
("decimal_int_conversion_wide", [
i64::MIN, i64::MAX, i64::from(i32::MIN) - 1, i64::from(u32::MAX) + 1,
-1234567890123456789, 1234567890123456789, -4294967296, 4294967296,
]),
] {
let values = (0..256).map(|index| numbers[index % numbers.len()]).collect::<Vec<_>>();
compare(name, &values, 1000, converted, converted_reference, |value| *value);
if name.ends_with("wide") {
compare_named("decimal_int_conversion_wide_direct", &values, 1000, converted, converted_direct_reference, |value| *value, "rust_direct");
}
}
}