汽车
java c(大数运算的性能差异Java、C和Rust)

背景

大数计算在金融行业比较普遍。计算机领域对浮点数的表达方式基本遵循如下机制。


IEEE 754

基于IEEE 754的浮点数表示方法如下:


因此总有极限,在超过极限之后,需要引入第三方库协助处理。大体如下

当浮点数精度超过20位,需要大数计算

当参与运算的数无法使用常规变量类型来表示时,例如超出如下范围时:


C的基础类型


Java的基础类型


Rust的基础类型

可以看到在大约20位精度后,需要家诶住第扩展类型或第三方库。Java可以使用BigInteger,BIgDecimal来表示;C可以借助GMP库来表示或运算;Rust可以基于自身的库或对照C来;那么性能差异到底如何?

最间实践方案


环境

单线程计算100次30位以上大数的加减乘除。所在的机器配置如下:

机器环境


Java

Java标准库中的 java.math.BigInteger 和 java.math.BigDecimal 可以用来处理大数运算。例如

import java.math.BigInteger;import java.math.BigDecimal;// BigInteger 示例BigInteger bigInt1 = new BigInteger("123456789012345678901234567890");BigInteger bigInt2 = new BigInteger("987654321098765432109876543210");BigInteger sum = bigInt1.add(bigInt2); // 加法:cite[2]:cite[6]// BigDecimal 示例(务必使用String构造器避免初始精度损失:cite[8])BigDecimal bd1 = new BigDecimal("0.09");BigDecimal bd2 = new BigDecimal("0.01");BigDecimal result = bd1.add(bd2); // 结果为精确的0.10:cite[8]


GMP

基于C语言有一个知名的大数运算库gmp,也可以对正相互和浮点数进行处理,如下:

#include "gmp.h"void gmp_float_test(){    mpf_t x, y, result;  // 声明mpf_t浮点变量    mpf_init2(x, 100);   // 初始化x,设置100位有效数字    mpf_init2(y, 100);   // 初始化y,100位有效数字    mpf_init2(result, 100); // 初始化结果变量,100位有效数字    // 赋值:将数值(或字符串)赋值给mpf_t变量    mpf_set_d(x, 3.14159265358979323846);  // 从double赋值    mpf_set_str(y, "2.71828182845904523536", 10);  // 从字符串赋值(避免double精度损失)    // 浮点运算:加法    mpf_add(result, x, y);  // result = x + y    // 输出结果(指定以10进制打印,保留50位小数)    gmp_printf("x + y = %.50Ff\n", result);    // 释放内存(必须手动释放,否则内存泄漏)    mpf_clear(x);    mpf_clear(y);    mpf_clear(result);}void gmp_int_test() {     mpz_t a, b, result;    // 初始化变量    mpz_init(a);    mpz_init(b);    mpz_init(result);    // 设置大整数值    mpz_set_str(a, "12345678901234567890", 10);    mpz_set_str(b, "98765432109876543210", 10);    // 加法    mpz_add(result, a, b);    gmp_printf("算式: %Zd + %Zd = %Zd\n", a, b, result);    // 乘法    mpz_mul(result, a, b);    gmp_printf("算式: %Zd * %Zd = %Zd\n", a, b, result);    // 除法    mpz_set_str(a, "123456789012345678901234567890123456789012345678901234567890", 10);    mpz_tdiv_q(result, a, b);    gmp_printf("算式: %Zd / %Zd = %Zd\n", a, b, result);    // 平方    mpz_pow_ui(result, a, 2);    gmp_printf("算式: %Zd^2 = %Zd\n", a, result);    // 清理内存    mpz_clear(a);    mpz_clear(b);    mpz_clear(result);}

Java+JNI+so(C+GMP)

初步测试,看到直接使用GMP的C代码运行效率大约比Java BigDecimal高出6倍以上。考虑将这部分运算使用C实现,看能否达到提高效率的目的。



Java BigDecimal计算100万次 +-*/


Rust、Java、C

Rust基于num、Java基于BigDecimal、C基于GMP,都提供相同的命令行参数指定计算的次数、参数,输出ns耗时。结果如下:


C最快,Rust相当不俗

相关代码如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <gmp.h>void factorial(mpz_t result, unsigned long n) {    mpz_set_ui(result, 1);    for (unsigned long i = 2; i <= n; i++) {        mpz_mul_ui(result, result, i);    }}void power(mpz_t result, const mpz_t base, unsigned long exp) {    mpz_set_ui(result, 1);    for (unsigned long i = 0; i < exp; i++) {        mpz_mul(result, result, base);    }}void fibonacci(mpz_t result, unsigned long n) {    if (n == 0) {        mpz_set_ui(result, 0);        return;    }    mpz_t a, b, temp;    mpz_init(a);    mpz_init(b);    mpz_init(temp);    mpz_set_ui(a, 0);    mpz_set_ui(b, 1);    for (unsigned long i = 2; i <= n; i++) {        mpz_add(temp, a, b);        mpz_set(a, b);        mpz_set(b, temp);    }    mpz_set(result, b);    mpz_clear(a);    mpz_clear(b);    mpz_clear(temp);}int main(int argc, char *argv[]) {    if (argc < 4) {        fprintf(stderr, "用法: %s <操作> <数字> <次数>\n", argv[0]);        fprintf(stderr, "操作选项:\n");        fprintf(stderr, "  factorial - 计算阶乘\n");        fprintf(stderr, "  power     - 计算幂运算(数字^次数)\n");        fprintf(stderr, "  fibonacci - 计算斐波那契数列\n");        fprintf(stderr, "\n示例:\n");        fprintf(stderr, "  %s factorial 100 1000\n", argv[0]);        fprintf(stderr, "  %s power 123456789 1000\n", argv[0]);        fprintf(stderr, "  %s fibonacci 1000 1000\n", argv[0]);        return 1;    }    char *operation = argv[1];    unsigned long number = strtoul(argv[2], NULL, 10);    unsigned long iterations = strtoul(argv[3], NULL, 10);    printf("C (GMP) 大数运算测试\n");    printf("操作: %s, 数字: %lu, 次数: %lu\n", operation, number, iterations);    struct timespec start, end;    clock_gettime(CLOCK_MONOTONIC, &start);    mpz_t result;    mpz_init(result);    if (strcmp(operation, "factorial") == 0) {        for (unsigned long i = 0; i < iterations; i++) {            factorial(result, number);        }        clock_gettime(CLOCK_MONOTONIC, &end);        unsigned long long duration = (end.tv_sec - start.tv_sec) * 1000000000ULL + (end.tv_nsec - start.tv_nsec);        gmp_printf("%lu! = %Zd\n", number, result);        printf("结果位数: %d\n", mpz_sizeinbase(result, 10));        printf("耗时: %llu ns\n", duration);    } else if (strcmp(operation, "power") == 0) {        mpz_t base;        mpz_init(base);        mpz_set_ui(base, number);        for (unsigned long i = 0; i < iterations; i++) {            power(result, base, iterations);        }        clock_gettime(CLOCK_MONOTONIC, &end);        unsigned long long duration = (end.tv_sec - start.tv_sec) * 1000000000ULL + (end.tv_nsec - start.tv_nsec);        gmp_printf("%lu^%lu = %Zd\n", number, iterations, result);        printf("结果位数: %d\n", mpz_sizeinbase(result, 10));        printf("耗时: %llu ns\n", duration);        mpz_clear(base);    } else if (strcmp(operation, "fibonacci") == 0) {        for (unsigned long i = 0; i < iterations; i++) {            fibonacci(result, number);        }        clock_gettime(CLOCK_MONOTONIC, &end);        unsigned long long duration = (end.tv_sec - start.tv_sec) * 1000000000ULL + (end.tv_nsec - start.tv_nsec);        gmp_printf("fibonacci(%lu) = %Zd\n", number, result);        printf("结果位数: %d\n", mpz_sizeinbase(result, 10));        printf("耗时: %llu ns\n", duration);    } else {        fprintf(stderr, "错误:未知的操作 '%s'\n", operation);        mpz_clear(result);        return 1;    }    mpz_clear(result);    return 0;}
import java.math.BigDecimal;import java.math.BigInteger;import java.math.MathContext;import java.time.Duration;import java.time.Instant;public class BigNumber {    public static BigDecimal factorial(int n) {        BigDecimal result = BigDecimal.ONE;        for (int i = 2; i <= n; i++) {            result = result.multiply(BigDecimal.valueOf(i));        }        return result;    }    public static BigDecimal power(BigDecimal base, long exp) {        BigDecimal result = BigDecimal.ONE;        for (long i = 0; i < exp; i++) {            result = result.multiply(base);        }        return result;    }    public static BigInteger fibonacci(int n) {        if (n == 0) {            return BigInteger.ZERO;        }        BigInteger a = BigInteger.ZERO;        BigInteger b = BigInteger.ONE;        for (int i = 2; i <= n; i++) {            BigInteger temp = a.add(b);            a = b;            b = temp;        }        return b;    }    public static void main(String[] args) {        if (args.length < 3) {            System.err.println("用法: java BigNumber <操作> <数字> <次数>");            System.err.println("操作选项:");            System.err.println("  factorial - 计算阶乘");            System.err.println("  power     - 计算幂运算(数字^次数)");            System.err.println("  fibonacci - 计算斐波那契数列");            System.err.println("\n示例:");            System.err.println("  java BigNumber factorial 100 1000");            System.err.println("  java BigNumber power 123456789 1000");            System.err.println("  java BigNumber fibonacci 1000 1000");            System.exit(1);        }        String operation = args[0];        int number;        long iterations;        try {            number = Integer.parseInt(args[1]);            iterations = Long.parseLong(args[2]);        } catch (NumberFormatException e) {            System.err.println("错误:无效的数字或次数");            System.exit(1);            return;        }        System.out.println("Java 大数运算测试");        System.out.println("操作: " + operation + ", 数字: " + number + ", 次数: " + iterations);        Instant start = Instant.now();        switch (operation) {            case "factorial":                BigDecimal factorialResult = BigDecimal.ZERO;                for (long i = 0; i < iterations; i++) {                    factorialResult = factorial(number);                }                Duration factorialDuration = Duration.between(start, Instant.now());                System.out.println(number + "! = " + factorialResult);                System.out.println("结果位数: " + factorialResult.toPlainString().length());                System.out.println("耗时: " + factorialDuration.tonanos() + " ns");                break;            case "power":                BigDecimal base = new BigDecimal(number);                BigDecimal powerResult = BigDecimal.ZERO;                for (long i = 0; i < iterations; i++) {                    powerResult = power(base, iterations);                }                Duration powerDuration = Duration.between(start, Instant.now());                System.out.println(number + "^" + iterations + " = " + powerResult);                System.out.println("结果位数: " + powerResult.toPlainString().length());                System.out.println("耗时: " + powerDuration.tonanos() + " ns");                break;            case "fibonacci":                BigInteger fibResult = BigInteger.ZERO;                for (long i = 0; i < iterations; i++) {                    fibResult = fibonacci(number);                }                Duration fibDuration = Duration.between(start, Instant.now());                System.out.println("fibonacci(" + number + ") = " + fibResult);                System.out.println("结果位数: " + fibResult.toString().length());                System.out.println("耗时: " + fibDuration.tonanos() + " ns");                break;            default:                System.err.println("错误:未知的操作 '" + operation + "'");                System.exit(1);        }    }}
use std::time::Instant;use num::BigInt;use std::env;fn factorial(n: u64) -> BigInt {    let mut result = BigInt::from(1);    for i in 2..=n {        result *= BigInt::from(i);    }    result}fn power(base: &BigInt, exp: u64) -> BigInt {    let mut result = BigInt::from(1);    for _ in 0..exp {        result *= base;    }    result}fn fibonacci(n: u64) -> BigInt {    if n == 0 {        return BigInt::from(0);    }    let mut a = BigInt::from(0);    let mut b = BigInt::from(1);    for _ in 2..=n {        let temp = &a + &b;        a = b;        b = temp;    }    b}fn main() {    let args: Vec<String> = env::args().collect();    if args.len() < 4 {        eprintln!("用法: {} <操作> <数字> <次数>", args[0]);        eprintln!("操作选项:");        eprintln!("  factorial - 计算阶乘");        eprintln!("  power     - 计算幂运算(数字^次数)");        eprintln!("  fibonacci - 计算斐波那契数列");        eprintln!("\n示例:");        eprintln!("  {} factorial 100 1000", args[0]);        eprintln!("  {} power 123456789 1000", args[0]);        eprintln!("  {} fibonacci 1000 1000", args[0]);        std::process::exit(1);    }    let operation = &args[1];    let number: u64 = match args[2].parse() {        Ok(n) => n,        Err(_) => {            eprintln!("错误:无效的数字 '{}'", args[2]);            std::process::exit(1);        }    };    let iterations: u64 = match args[3].parse() {        Ok(n) => n,        Err(_) => {            eprintln!("错误:无效的次数 '{}'", args[3]);            std::process::exit(1);        }    };    println!("Rust 大数运算测试");    println!("操作: {}, 数字: {}, 次数: {}", operation, number, iterations);    let start = Instant::now();    match operation.as_str() {        "factorial" => {            let mut result = BigInt::from(0);            for _ in 0..iterations {                result = factorial(number);            }            let duration = start.elapsed();            println!("{}! = {}", number, result);            println!("结果位数: {}", result.to_string().len());            println!("耗时: {} ns", duration.as_nanos());        }        "power" => {            let base = BigInt::from(number);            let mut result = BigInt::from(0);            for _ in 0..iterations {                result = power(&base, iterations);            }            let duration = start.elapsed();            println!("{}^{} = {}", number, iterations, result);            println!("结果位数: {}", result.to_string().len());            println!("耗时: {} ns", duration.as_nanos());        }        "fibonacci" => {            let mut result = BigInt::from(0);            for _ in 0..iterations {                result = fibonacci(number);            }            let duration = start.elapsed();            println!("fibonacci({}) = {}", number, result);            println!("结果位数: {}", result.to_string().len());            println!("耗时: {} ns", duration.as_nanos());        }        _ => {            eprintln!("错误:未知的操作 '{}'", operation);            std::process::exit(1);        }    }}

参考资料

  1. GMP官网 : https://gmplib.org/
  2. Java的BigDicimal BigInteger源码

顶一下()     踩一下()

热门推荐

发表评论
0评