Skip to content

&str parsing and div operation performance improvement #32

Description

@jsilhan

I needed a const evaluation of fastnum (which is really cool!) so I have replaced rust_decimal with fastnum in my project. Unfortunately, my app started using ~20% of CPU vs ~3% with rust_decimal before.

I have written a simple benchmark comparison of fastnum vs rust_decimal with the most used operations from my app and this is the output:

String parsing: Fastnum 222.69ms vs Rust Decimal 105.50ms (2.11x slower)
Addition: Fastnum 25.50ms vs Rust Decimal 9.08ms (2.81x slower)
Subtraction: Fastnum 27.15ms vs Rust Decimal 9.63ms (2.82x slower)
Multiplication: Fastnum 35.88ms vs Rust Decimal 8.85ms (4.05x slower)
Division: Fastnum 1435.93ms vs Rust Decimal 71.45ms (20.10x slower)

I don't know the details of both lib's implementation but I would guess the parsing of str should take the same time. Also div operation taking 20x as much seems to me a lot. Could the performance be improved please?

#[cfg(test)]
mod benchmark {
    use fastnum::{D128, dec128};
    use rust_decimal::Decimal;
    use std::str::FromStr;
    use std::time::Instant;

    #[test]
    fn fastnum_vs_rust_decimal_benchmark() {
        let iterations = 1_000_000;
        let a_fn = dec128!(789.012);
        let b_fn = dec128!(12.345);
        let a_rd = Decimal::from_str("789.012").unwrap();
        let b_rd = Decimal::from_str("12.345").unwrap();

        // String parsing
        let test_strings = vec!["123.456", "0.05", "-0.05", "1000000.123456789", "0.000001"];

        let start = Instant::now();
        for _ in 0..iterations {
            for s in &test_strings {
                let _res: D128 = s.parse().unwrap();
            }
        }
        let fastnum_str = start.elapsed();

        let start = Instant::now();
        for _ in 0..iterations {
            for s in &test_strings {
                let _res: Decimal = Decimal::from_str(s).unwrap();
            }
        }
        let rust_decimal_str = start.elapsed();

        // Addition
        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_fn + b_fn;
        }
        let fastnum_add = start.elapsed();

        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_rd + b_rd;
        }
        let rust_decimal_add = start.elapsed();

        // Subtraction
        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_fn - b_fn;
        }
        let fastnum_sub = start.elapsed();

        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_rd - b_rd;
        }
        let rust_decimal_sub = start.elapsed();

        // Multiplication
        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_fn * b_fn;
        }
        let fastnum_mul = start.elapsed();
        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_rd * b_rd;
        }
        let rust_decimal_mul = start.elapsed();

        // Division
        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_fn / b_fn;
        }
        let fastnum_div = start.elapsed();

        let start = Instant::now();
        for _ in 0..iterations {
            let _res = a_rd / b_rd;
        }
        let rust_decimal_div = start.elapsed();

        println!("=== Fastnum D128 vs Rust Decimal Performance ===");
        println!(
            "String parsing: Fastnum {:.2}ms vs Rust Decimal {:.2}ms ({:.2}x slower)",
            fastnum_str.as_nanos() as f64 / 1_000_000.0,
            rust_decimal_str.as_nanos() as f64 / 1_000_000.0,
            fastnum_str.as_nanos() as f64 / rust_decimal_str.as_nanos() as f64
        );
        println!(
            "Addition: Fastnum {:.2}ms vs Rust Decimal {:.2}ms ({:.2}x slower)",
            fastnum_add.as_nanos() as f64 / 1_000_000.0,
            rust_decimal_add.as_nanos() as f64 / 1_000_000.0,
            fastnum_add.as_nanos() as f64 / rust_decimal_add.as_nanos() as f64
        );
        println!(
            "Subtraction: Fastnum {:.2}ms vs Rust Decimal {:.2}ms ({:.2}x slower)",
            fastnum_sub.as_nanos() as f64 / 1_000_000.0,
            rust_decimal_sub.as_nanos() as f64 / 1_000_000.0,
            fastnum_sub.as_nanos() as f64 / rust_decimal_sub.as_nanos() as f64
        );
        println!(
            "Multiplication: Fastnum {:.2}ms vs Rust Decimal {:.2}ms ({:.2}x slower)",
            fastnum_mul.as_nanos() as f64 / 1_000_000.0,
            rust_decimal_mul.as_nanos() as f64 / 1_000_000.0,
            fastnum_mul.as_nanos() as f64 / rust_decimal_mul.as_nanos() as f64
        );
        println!(
            "Division: Fastnum {:.2}ms vs Rust Decimal {:.2}ms ({:.2}x slower)",
            fastnum_div.as_nanos() as f64 / 1_000_000.0,
            rust_decimal_div.as_nanos() as f64 / 1_000_000.0,
            fastnum_div.as_nanos() as f64 / rust_decimal_div.as_nanos() as f64
        );
    }
}

related #12

Metadata

Metadata

Assignees

Labels

performanceProblems and improvements related to performance.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions