说明:以下是在同一 Linux 容器内完成。容器有资源限制,因此“看到的 CPU 核数/内存”与宿主机不完全一致。
DOTNET_GCServer=1-Xms1g -Xmx1g(把 Java 堆固定为 1GB,避免堆动态扩张干扰)-XX:+UseG1GC-XX:+AlwaysPreTouch(尽量减少运行中页分配扰动)
import java.util.*; public class BigIntBench { // SplitMix64 RNG (deterministic, fast) static final class SplitMix64 { private long x; SplitMix64( long seed) { this .x = seed; } long nextLong () { long z = (x += 0x9E3779B97F4A7C15L ); z = (z ^ (z >>> 30 )) * 0xBF58476D1CE4E5B9L ; z = (z ^ (z >>> 27 )) * 0x94D049BB133111EBL ; return z ^ (z >>> 31 ); } void nextBytes ( byte[] dst) { int i = 0 ; while (i < dst.length) { long v = nextLong(); for ( int k = 0 ; k < 8 && i < dst.length; k++) { dst[i++] = ( byte )(v >>> ( 56 - 8 *k)); // big-endian stream } } } } static BigInteger[] genBigInts( int bitSize, int count, long seed) { SplitMix64 rng = new SplitMix64 (seed); int byteLen = (bitSize + 7 ) / 8 ; BigInteger[] arr = new BigInteger [count]; byte [] buf = new byte [byteLen]; int topBit = (bitSize - 1 ) % 8 ; int keepBits = topBit + 1 ; int firstMask = (keepBits == 8 ) ? 0xFF : (( 1 << keepBits) - 1 ); byte topMask = ( byte )( 1 << topBit); for ( int i = 0 ; i < count; i++) { rng.nextBytes(buf); // Ensure exact bit length: // - mask away unused top bits when bitSize is not byte-aligned // - set the top bit so the number has the requested bit length buf[ 0 ] &= ( byte )firstMask; buf[ 0 ] |= topMask; arr[i] = new BigInteger ( 1 , buf); } return arr; } static BigInteger genModulus ( int bitSize, long seed) { SplitMix64 rng = new SplitMix64 (seed); int byteLen = (bitSize + 7 ) / 8 ; byte [] buf = new byte [byteLen]; rng.nextBytes(buf); int topBit = (bitSize - 1 ) % 8 ; int keepBits = topBit + 1 ; int firstMask = (keepBits == 8 ) ? 0xFF : (( 1 << keepBits) - 1 ); buf[ 0 ] &= ( byte )firstMask; buf[ 0 ] |= ( byte )( 1 << topBit); buf[buf.length - 1 ] |= 1 ; // odd return new BigInteger ( 1 , buf); } enum Op { ADD_MOD, MUL_MOD, MODPOW } static final class Result { final String lang; final int bits; final Op op; final long ops; final double nsPerOp; final long checksum; Result(String lang, int bits, Op op, long ops, double nsPerOp, long checksum) { this .lang = lang; this .bits = bits; this .op = op; this .ops = ops; this .nsPerOp = nsPerOp; this .checksum = checksum; } String toJson () { return String.format(Locale.ROOT, "{\"lang\":\"%s\",\"bits\":%d,\"op\":\"%s\",\"ops\":%d,\"nsPerOp\":%.3f,\"checksum\":%d}" , lang, bits, op.name(), ops, nsPerOp, checksum); } } static long runOnce (Op op, BigInteger[] a, BigInteger[] b, BigInteger[] e, BigInteger mod, int outer) { BigInteger acc = BigInteger.ZERO; int n = a.length; switch (op) { case ADD_MOD: for ( int o = 0 ; o < outer; o++) { for ( int i = 0 ; i < n; i++) { BigInteger r = a[i].add(b[i]).mod(mod); acc = acc.xor(r); } } break ; case MUL_MOD: for ( int o = 0 ; o < outer; o++) { for ( int i = 0 ; i < n; i++) { BigInteger r = a[i].multiply(b[i]).mod(mod); acc = acc.xor(r); } } break ; case MODPOW: // here we use a.length as n; e can be same length for ( int o = 0 ; o < outer; o++) { for ( int i = 0 ; i < n; i++) { BigInteger r = a[i].modPow(e[i], mod); acc = acc.xor(r); } } break ; default : throw new IllegalArgumentException ( "Unknown op: " + op); } return acc.longValue(); } static Result bench (String lang, int bits, Op op, BigInteger[] a, BigInteger[] b, BigInteger[] e, BigInteger mod, long targetOps, int warmups, int measures) { int n = a.length; int outer = ( int )Math.max( 1 , targetOps / n); long actualOps = ( long )n * outer; // warmup long ck = 0 ; for ( int i = 0 ; i < warmups; i++) { ck ^= runOnce(op, a, b, e, mod, outer); } long [] times = new long [measures]; for ( int i = 0 ; i < measures; i++) { long t0 = System.nanoTime(); long c = runOnce(op, a, b, e, mod, outer); long t1 = System.nanoTime(); ck ^= c; times[i] = (t1 - t0); } Arrays.sort(times); long median = times[times.length / 2 ]; double nsPerOp = ( double )median / ( double )actualOps; return new Result (lang, bits, op, actualOps, nsPerOp, ck); } static void printHuman (List<Result> results) { System.out.println( "Java BigInteger benchmark" ); System.out.println( "java.version=" + System.getProperty( "java.version" )); System.out.println( "java.vm.name=" + System.getProperty( "java.vm.name" )); System.out.println(); System.out.printf(Locale.ROOT, "%-6s %-9s %-12s %-12s\n" , "Bits" , "Op" , "ns/op(med)" , "ops/run" ); for (Result r : results) { System.out.printf(Locale.ROOT, "%-6d %-9s %-12.3f %-12d\n" , r.bits, r.op.name(), r.nsPerOp, r.ops); } System.out.println(); System.out.println( "checksum=" + results.stream().mapToLong(x -> x.checksum).reduce( 0L , (x,y)->x^y)); } public static void main (String[] args) { boolean json = false ; for (String a : args) if (a.equals( "--json" )) json = true ; int warmups = 5 ; int measures = 11 ; int [] bitSizes = new int []{ 256 , 1024 , 4096 }; List<Result> results = new ArrayList <>(); for ( int bits : bitSizes) { BigInteger mod = genModulus(bits, 0xA1B2C3D4E5F60708L ^ bits); // add/mul datasets int nAddMul = 1024 ; BigInteger[] a = genBigInts(bits, nAddMul, 0x1111222233334444L ^ bits); BigInteger[] b = genBigInts(bits, nAddMul, 0x9999AAAABBBBCCCCL ^ bits); // modpow datasets (smaller) int nPow = 256 ; BigInteger[] ap = genBigInts(bits, nPow, 0x13579BDF2468ACE0L ^ bits); BigInteger[] ep = genBigInts(Math.min(bits, 512 ), nPow, 0x0FEDCBA987654321L ^ bits); long addOps; long mulOps; long powOps; if (bits == 256 ) { addOps = 2_000_000L ; mulOps = 500_000L ; powOps = 8_000L ; } else if (bits == 1024 ) { addOps = 1_000_000L ; mulOps = 120_000L ; powOps = 1_500L ; } else { addOps = 200_000L ; mulOps = 20_000L ; powOps = 250L ; } results.add(bench( "java" , bits, Op.ADD_MOD, a, b, null , mod, addOps, warmups, measures)); results.add(bench( "java" , bits, Op.MUL_MOD, a, b, null , mod, mulOps, warmups, measures)); results.add(bench( "java" , bits, Op.MODPOW, ap, null , ep, mod, powOps, warmups, measures)); } if (json) { for (Result r : results) System.out.println(r.toJson()); sing System.Diagnostics; using System.Globalization; using System.Linq; using System.Numerics; sealed class SplitMix64 { private ulong _x; public SplitMix64( ulong seed) => _x = seed; public ulong NextUInt64() { ulong z = (_x += 0x9E3779B97F4A7C15 UL); z = (z ^ (z >> 30 )) * 0xBF58476D1CE4E5B9 UL; z = (z ^ (z >> 27 )) * 0x94D049BB133111EB UL; return z ^ (z >> 31 ); } public void NextBytes( byte[] dst) { int i = 0 ; while (i < dst.Length) { ulong v = NextUInt64(); for ( int k = 0 ; k < 8 && i < dst.Length; k++) { dst[i++] = ( byte )(v >> ( 56 - 8 * k)); // big-endian stream } } } } enum Op { ADD_MOD, MUL_MOD, MODPOW } record Result ( string Lang , int Bits , Op Op , long Ops , double NsPerOp , long Checksum ) { public string ToJson() => string .Create(CultureInfo.InvariantCulture, $"{{\"lang\":\" {Lang}\",\"bits\": {Bits},\"op\":\" {Op}\",\"ops\": {Ops},\"nsPerOp\": {NsPerOp:F3},\"checksum\": {Checksum}}}" ); } static class BigIntBench { static BigInteger[] GenBigInts( int bitSize, int count, ulong seed) { var rng = new SplitMix64(seed); int byteLen = (bitSize + 7 ) / 8 ; var arr = new BigInteger[count]; var buf = new byte [byteLen]; int topBit = (bitSize - 1 ) % 8 ; byte topMask = ( byte )( 1 << topBit); for ( int i = 0 ; i < count; i++) { rng.NextBytes(buf); buf[ 0 ] |= topMask; // unsigned + big-endian prevents negative arr[i] = new BigInteger(buf, isUnsigned: true , isBigEndian: true ); } return arr; } static BigInteger GenModulus( int bitSize, ulong seed) { var rng = new SplitMix64(seed); int byteLen = (bitSize + 7 ) / 8 ; var buf = new byte [byteLen]; rng.NextBytes(buf); int topBit = (bitSize - 1 ) % 8 ; buf[ 0 ] |= ( byte )( 1 << topBit); buf[^ 1 ] |= 1 ; // odd return new BigInteger(buf, isUnsigned: true , isBigEndian: true ); } static long RunOnce( Op op, BigInteger[] a, BigInteger[] b, BigInteger[] e, BigInteger mod, int outer) { BigInteger acc = BigInteger.Zero; int n = a.Length; switch (op) { case Op.ADD_MOD: for ( int o = 0 ; o < outer; o++) for ( int i = 0 ; i < n; i++) { var r = (a[i] + b[i]) % mod; acc ^= r; } break ; case Op.MUL_MOD: for ( int o = 0 ; o < outer; o++) for ( int i = 0 ; i < n; i++) { var r = (a[i] * b[i]) % mod; acc ^= r; } break ; case Op.MODPOW: for ( int o = 0 ; o < outer; o++) for ( int i = 0 ; i < n; i++) { var r = BigInteger.ModPow(a[i], e[i], mod); acc ^= r; } break ; } return ( long )(acc & long .MaxValue); // stable checksum } static Result Bench( string lang, int bits, Op op, BigInteger[] a, BigInteger[] b, BigInteger[] e, BigInteger mod, long targetOps, int warmups, int measures) { int n = a.Length; int outer = ( int )Math.Max( 1 , targetOps / n); long actualOps = ( long )n * outer; long ck = 0 ; for ( int i = 0 ; i < warmups; i++) ck ^= RunOnce(op, a, b, e, mod, outer); long [] timesNs = new long [measures]; for ( int i = 0 ; i < measures; i++) { var sw = Stopwatch.StartNew(); long c = RunOnce(op, a, b, e, mod, outer); sw.Stop(); ck ^= c; // Stopwatch ticks to ns timesNs[i] = ( long )(sw.ElapsedTicks * ( 1 _000_000_000 .0 / Stopwatch.Frequency)); } Array.Sort(timesNs); long median = timesNs[timesNs.Length / 2 ]; double nsPerOp = ( double )median / actualOps; return new Result(lang, bits, op, actualOps, nsPerOp, ck); } static void PrintHuman( List<Result> results) { Console.WriteLine( "C# BigInteger benchmark" ); Console.WriteLine( $"dotnet.version= {Environment.Version}" ); Console.WriteLine( $"os= {System.Runtime.InteropServices.RuntimeInformation.OSDescription}" ); Console.WriteLine( $"arch= {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}" ); Console.WriteLine(); zjtgjx.net Console.WriteLine( $" { "Bits", -6} { "Op", -9} { "ns/op(med)", -12} { "ops/run", -12}" ); foreach ( var r in results) Console.WriteLine( string .Create(CultureInfo.InvariantCulture, $" {r.Bits, -6} {r.Op, -9} {r.NsPerOp, -12:F3} {r.Ops, -12}" )); Console.WriteLine(); long checksum = 0 ; foreach ( var r in results) checksum ^= r.Checksum; Console.WriteLine( $"checksum= {checksum}" ); } public static int Main( string[] args) { bool json = args.Any(a => a == "--json" ); int warmups = 5 ; int measures = 11 ; int [] bitSizes = [ 256 , 1024 , 4096 ]; var results = new List<Result>(); foreach ( int bits in bitSizes) { var mod = GenModulus(bits, 0xA1B2C3D4E5F60708 UL ^ ( uint )bits); int nAddMul = 1024 ; var a = GenBigInts(bits, nAddMul, 0x1111222233334444 UL ^ ( uint )bits); var b = GenBigInts(bits, nAddMul, 0x9999AAAABBBBCCCC UL ^ ( uint )bits); int nPow = 256 ; var ap = GenBigInts(bits, nPow, 0x13579BDF2468ACE0 UL ^ ( uint )bits); var ep = GenBigInts(Math.Min(bits, 512 ), nPow, 0x0FEDCBA987654321 UL ^ ( uint )bits); long addOps = bits switch { 256 => 2 _000_000L, 1024 => 1 _000_000L, _ => 200 _000L }; long mulOps = bits switch { 256 => 500 _000L, 1024 => 120 _000L, _ => 20 _000L }; long powOps = bits switch { 256 => 8 _000L, 1024 => 1 _500L, _ => 250L }; results.Add(Bench( "csharp" , bits, Op.ADD_MOD, a, b, null !, mod, addOps, warmups, measures)); results.Add(Bench( "csharp" , bits, Op.MUL_MOD, a, b, null !, mod, mulOps, warmups, measures)); results.Add(Bench( "csharp" , bits, Op.MODPOW, ap, null !, ep, mod, powOps, warmups, measures)); } if (json) { foreach ( var r in results) Console.WriteLine(r.ToJson());
d /app/bigintbench/java javac BigIntBench.java java -Xms1g -Xmx1g -XX:+UseG1GC -XX:+AlwaysPreTouch BigIntBench --json # .NET 10 cd /app/bigintbench/csharp dotnet build -c Release
{"lang":"java","bits":256,"op":"ADD_MOD","ops":1999872,"nsPerOp":139.589,"checksum":0} {"lang":"java","bits":256,"op":"MUL_MOD","ops":499712,"nsPerOp":387.031,"checksum":0} {"lang":"java","bits":256,"op":"MODPOW","ops":7936,"nsPerOp":17764.884,"checksum":0} {"lang":"java","bits":1024,"op":"ADD_MOD","ops":999424,"nsPerOp":284.672,"checksum":0} {"lang":"java","bits":1024,"op":"MUL_MOD","ops":119808,"nsPerOp":3094.540,"checksum":0} {"lang":"java","bits":1024,"op":"MODPOW","ops":1280,"nsPerOp":264577.852,"checksum":0} {"lang":"java","bits":4096,"op":"ADD_MOD","ops":199680,"nsPerOp":900.735,"checksum":0} {"lang":"java","bits":4096,"op":"MUL_MOD","ops":19456,"nsPerOp":32062.554,"checksum":0}
{"lang":"csharp","bits":256,"op":"MUL_MOD","ops":499712,"nsPerOp":560.246,"checksum":0} {"lang":"csharp","bits":256,"op":"MODPOW","ops":7936,"nsPerOp":169713.608,"checksum":0} {"lang":"csharp","bits":1024,"op":"ADD_MOD","ops":999424,"nsPerOp":297.335,"checksum":0} {"lang":"csharp","bits":1024,"op":"MUL_MOD","ops":119808,"nsPerOp":4792.760,"checksum":0} {"lang":"csharp","bits":1024,"op":"MODPOW","ops":1280,"nsPerOp":1938407.720,"checksum":0} {"lang":"csharp","bits":4096,"op":"ADD_MOD","ops":199680,"nsPerOp":1280.760,"checksum":0} {"lang":"csharp","bits":4096,"op":"MUL_MOD","ops":19456,"nsPerOp":36894.568,"checksum":0}
# 编译(Java 8) /path/to/jdk8/bin/javac BigIntBench.java # 运行(Java 8) /path/to/jdk8/bin/java -Xms1g -Xmx1g -XX:+UseG1GC -XX:+AlwaysPreTouch BigIntBench --json # 运行(Java 21)
{"lang":"java","bits":256,"op":"ADD_MOD","ops":1999872,"nsPerOp":207.335,"checksum":0} {"lang":"java","bits":256,"op":"MUL_MOD","ops":499712,"nsPerOp":468.248,"checksum":0} {"lang":"java","bits":256,"op":wap.bakmk.com "MODPOW","ops":7936,"nsPerOp":17697.113,"checksum":0} {"lang":"java","bits":1024,"op":"ADD_MOD","ops":999424,"nsPerOp":390.906,"checksum":0} {"lang":"java","bits":1024,"op":"MUL_MOD","ops":119808,"nsPerOp":3089.474,"checksum":0} {"lang":"java","bits":1024,"op":"MODPOW","ops":1280,"nsPerOp":277395.652,"checksum":0} {"lang":"java","bits":4096,"op":"ADD_MOD","ops":199680,"nsPerOp":990.708,"checksum":0} {"lang":"java","bits":4096,"op":"MUL_MOD","ops":19456,"nsPerOp":30692.214,"checksum":0}
在寻找 .NET 大数性能优化方案的过程中,我们自然能想到了业界标杆 GMP ——它是 GNU Multi-Precision Arithmetic Library,很多数学软件/密码学实现都会用它做高性能大整数运算。
我碰巧也为它做了一个 .NET 封装: Sdcb.Arithmetic。
但必须提前声明: 让 GMP 作为“外援”加入这场对比,是“不公平”的。原因很简单:
BigInteger 则是不可变对象。
using
System.Collections.Generic;
using
System.Diagnostics;
using
System.Globalization;
using
System.Linq;
using
System.Numerics;
using
Sdcb.Arithmetic.Gmp;
sealed
class
SplitMix64
{
private
ulong
_x;
public
SplitMix64(
ulong seed)
=> _x = seed;
public
ulong
NextUInt64()
{
ulong
z = (_x +=
0x9E3779B97F4A7C15
UL);
z = (z ^ (z >>
30
)) *
0xBF58476D1CE4E5B9
UL;
z = (z ^ (z >>
27
)) *
0x94D049BB133111EB
UL;
return
z ^ (z >>
31
);
}
public
void
NextBytes(
byte[] dst)
{
int
i =
0
;
while
(i < dst.Length)
{
ulong
v = NextUInt64();
for
(
int
k =
0
; k <
8
&& i < dst.Length; k++)
dst[i++] = (
byte
)(v >> (
56
-
8
* k));
}
}
}
enum
Op { ADD_MOD, MUL_MOD, MODPOW }
record
Result
(
string
Impl
,
int
Bits
,
Op
Op
,
long
Ops
,
double
NsPerOp
,
long
Checksum
)
{
public
string
ToJson()
=>
string
.Create(CultureInfo.InvariantCulture,
$"{{\"lang\":\"
{Impl}\",\"bits\":
{Bits},\"op\":\"
{Op}\",\"ops\":
{Ops},\"nsPerOp\":
{NsPerOp:F3},\"checksum\":
{Checksum}}}"
);
}
static
class
BenchUtil
{
public
static
void
MaskToBitSize(
byte[] buf,
int bitSize)
{
int
topBit =www.szfort.com.cn (bitSize -
1
) %
8
;
int
keepBits = topBit +
1
;
int
firstMask = keepBits ==
8
?
0xFF
: ((
1
<< keepBits) -
1
);
buf[
0
] &= (
byte
)firstMask;
buf[
0
] |= (
byte
)(
1
<< topBit);
}
public
static
string
ToHex(
byte[] bytes)
=> Convert.ToHexString(bytes);
}
static
class
BigIntegerBench
{
public
static BigInteger[]
Gen(
int bitSize,
int count,
ulong seed)
{
var
rng =
new
SplitMix64(seed);
int
byteLen = (bitSize +
7
) /
8
;
var
arr =
new
BigInteger[count];
var
buf =
new
byte
[byteLen];
for
(
int
i =
0
; i < count; i++)
{
rng.NextBytes(buf);
BenchUtil.MaskToBitSize(buf, bitSize);
arr[i] =
new
BigInteger(buf, isUnsigned:
true
, isBigEndian:
true
);
}
return
arr;
}
public
static BigInteger
GenModulus(
int bitSize,
ulong seed)
{
var
rng =
new
SplitMix64(seed);
int
byteLen = (bitSize +
7
) /
8
;
var
buf =
new
byte
[byteLen];
rng.NextBytes(buf);
BenchUtil.MaskToBitSize(buf, bitSize);
buf[^
1
] |=
1
;
return
new
BigInteger(buf, isUnsigned:
true
, isBigEndian:
true
);
}
public
static
long
RunOnce(
Op op, BigInteger[] a, BigInteger[] b, BigInteger[] e, BigInteger mod,
int outer)
{
BigInteger acc = BigInteger.Zero;
int
n = a.Length;
switch
(op)
{
case
Op.ADD_MOD:
for
(
int
o =
0
; o < outer; o++)
for
(
int
i =
0
; i < n; i++)
acc ^= (a[i] + b[i]) % mod;
break
;
case
Op.MUL_MOD:
for
(
int
o =
0
; o < outer; o++)
for
(
int
i =
0
; i < n; i++)
acc ^= (a[i] * b[i]) % mod;
break
;
case
Op.MODPOW:
for
(
int
o =
0
; o < outer; o++)
for
(
int
i =
0
; i < n; i++)
acc ^=wap.bakmk.com BigInteger.ModPow(a[i], e[i], mod);
break
;
}
return
(
long
)(acc &
long
.MaxValue);
}
}
static
class
GmpIntegerBench
{
public
static GmpInteger[]
Gen(
int bitSize,
int count,
ulong seed)
{
var
rng =
new
SplitMix64(seed);
int
byteLen = (bitSize +
7
) /
8
;
var
arr =
new
GmpInteger[count];
var
buf =
new
byte
[byteLen];
for
(
int
i =
0
; i < count; i++)
{
rng.NextBytes(buf);
BenchUtil.MaskToBitSize(buf, bitSize);
arr[i] = GmpInteger.Parse(BenchUtil.ToHex(buf),
16
);
}
return
arr;
}
public
static GmpInteger
GenModulus(
int bitSize,
ulong seed)
{
var
rng =
new
SplitMix64(seed);
int
byteLen = (bitSize +
7
) /
8
;
var
buf =
new
byte
[byteLen];
rng.NextBytes(buf);
BenchUtil.MaskToBitSize(buf, bitSize);
buf[^
1
] |=
1
;
return
GmpInteger.Parse(BenchUtil.ToHex(buf),
16
);
}
public
static
long
RunOnce(
Op op, GmpInteger[] a, GmpInteger[] b, GmpInteger[] e, GmpInteger mod,
int outer)
{
using
var
acc = GmpInteger.From(
0
);
using
var
tmp = GmpInteger.From(
0
);
int
n = a.Length;
switch
(op)
{
case
Op.ADD_MOD:
for
(
int
o =
0
; o < outer; o++)
for
(
int
i =
0
; i < n; i++)
{
GmpInteger.AddInplace(tmp, a[i], b[i]);
GmpInteger.ModInplace(tmp, tmp, mod);
GmpInteger.BitwiseXorInplace(acc, acc, tmp);
}
break
;
case
Op.MUL_MOD:
for
(
int
o =
0
; o < outer; o++)
for
(
int
i =
0
; i < n; i++)
{
GmpInteger.MultiplyInplace(tmp, a[i], b[i]);
GmpInteger.ModInplace(tmp, tmp, mod);
GmpInteger.BitwiseXorInplace(acc, acc, tmp);
}
break
; bakmk.com
case
Op.MODPOW:
for
(
int
o =
0
; o < outer; o++)
for
(
int
i =
0
; i < n; i++)
{
GmpInteger.PowerModInplace(tmp, a[i], e[i], mod);
GmpInteger.BitwiseXorInplace(acc, acc, tmp);
}
break
;
}
return
acc.GetHashCode();
}
public
static
void
DisposeAll(
GmpInteger[] xs)
{
foreach
(
var
x
in
xs) x.Dispose();
}
}
static
class
Runner
{
static Result
BenchBigInteger(
int bits, Op op, BigInteger[] a, BigInteger[] b, BigInteger[] e, BigInteger mod,
long targetOps,
int warmups,
int measures)
{
int
n = a.Length;
int
outer = (
int
)Math.Max(
1
, targetOps / n);
long
actualOps = (
long
)n * outer;
long
ck =
0
;
for
(
int
i =
0
; i < warmups; i++) ck ^= BigIntegerBench.RunOnce(op, a, b, e, mod, outer);
long
[] timesNs =
new
long
[measures];
for
(
int
i =
0
; i < measures; i++)
{
var
sw = Stopwatch.StartNew();
long
c = BigIntegerBench.RunOnce(op, a, b, e, mod, outer);
sw.Stop();
ck ^= c;
timesNs[i] = (
long
)(sw.ElapsedTicks * (
1
_000_000_000
.0
/ Stopwatch.Frequency));
}
Array.Sort(timesNs);
long
median = timesNs[timesNs.Length /
2
];
return
new
Result(
"csharp_bigint"
, bits, op, actualOps, (
double
)median / actualOps, ck);
}
static Result
BenchGmpInteger(
int bits, Op op, GmpInteger[] a, GmpInteger[] b, GmpInteger[] e, GmpInteger mod,
long targetOps,
int warmups,
int measures)
{
int
n = a.Length; m.jsastone.com.cn
int
outer = (
int
)Math.Max(
1
, targetOps / n);
long
actualOps = (
long
)n * outer;
long
ck =
0
;
for
(
int
i =
0
; i < warmups; i++) ck ^= GmpIntegerBench.RunOnce(op, a, b, e, mod, outer);
long
[] timesNs =
new
long
[measures];
for
(
int
i =
0
; i < measures; i++)
{
var
sw = Stopwatch.StartNew();
long
c = GmpIntegerBench.RunOnce(op, a, b, e, mod, outer);
sw.Stop();
ck ^= c;
timesNs[i] = (
long
)(sw.ElapsedTicks * (
1
_000_000_000
.0
/ Stopwatch.Frequency));
}
Array.Sort(timesNs);
long
median = timesNs[timesNs.Length /
2
];
return
new
Result(
"csharp_gmp_inplace"
, bits, op, actualOps, (
double
)median / actualOps, ck);
}
public
static
int
Run(
string[] args)
{
bool
json = args.Any(a => a ==
"--json"
);
int
warmups =
5
;
int
measures =
11
;
int
[] bitSizes = [
256
,
1024
,
4096
];
var
results =
new
List<Result>();
foreach
(
int
bits
in
bitSizes)
{
long
addOps = bits
switch
{
256
=>
2
_000_000L,
1024
=>
1
_000_000L, _ =>
200
_000L };
long
mulOps = bits
switch
{
256
=>
500
_000L,
1024
=>
120
_000L, _ =>
20
_000L };
long
powOps = bits
switch
{
256
=>
8
_000L,
1024
=>
1
_500L, _ =>
250L
};
// BigInteger data
var
modB BigIntegerBench.GenModulus(bits,
0xA1B2C3D4E5F60708
UL ^ (
uint
)bits);
var
aB = BigIntegerBench.Gen(bits,
1024
,
0x1111222233334444
UL ^ (
uint
)bits);
var
bB = BigIntegerBench.Gen(bits,
1024
,
0x9999AAAABBBBCCCC
UL ^ (
uint
)bits);
var
apB = BigIntegerBench.Gen(bits,
256
,
0x13579BDF2468ACE0
UL ^ (
uint
)bits);
var
epB = BigIntegerBench.Gen(Math.Min(bits,
512
),accfj.cn
256
,
0x0FEDCBA987654321
UL ^ (
uint
)bits);
// GmpInteger data (same seeds/bit sizes)
using
var
modG = GmpIntegerBench.GenModulus(bits,
0xA1B2C3D4E5F60708
UL ^ (
uint
)bits);
var
aG = GmpIntegerBench.Gen(bits,
1024
,
0x1111222233334444
UL ^ (
uint
)bits);
var
bG = GmpIntegerBench.Gen(bits,
1024
,
0x9999AAAABBBBCCCC
UL ^ (
uint
)bits);
var
apG = GmpIntegerBench.Gen(bits,
256
,
0x13579BDF2468ACE0
UL ^ (
uint
)bits);
var
epG = GmpIntegerBench.Gen(Math.Min(bits,
512
),
256
,
0x0FEDCBA987654321
UL ^ (
uint
)bits);
try
{
results.Add(BenchBigInteger wap.qyae.cn (bits, Op.ADD_MOD, aB, bB,
null
!, modB, addOps, warmups, measures));
results.Add(BenchBigInteger(bits, Op.MUL_MOD, aB, bB,
null
!, modB, mulOps, warmups, measures));
results.Add(BenchBigInteger wap.accfj.cn(bits, m.accfj.cn Op.MODPOW, apB,
null
!, epB, modB, powOps, warmups, measures));
results.Add(BenchGmpInteger jsastone.com.cn (bits, qyae.cn Op.ADD_MOD, aG, bG,
null
!, modG, addOps, warmups, measures));
results.Add(BenchGmpInteger(bits, Op.MUL_MOD, aG, bG,
null
!, modG, mulOps, warmups, measures));
results.Add(BenchGmpInteger(bits, Op.MODPOW, apG,
null
!, epG, modG, powOps, warmups, measures));
}
finally
{
GmpIntegerBench.DisposeAll(aG);
GmpIntegerBench.DisposeAll(bG);
GmpIntegerBench.DisposeAll(apG);
GmpIntegerBench.DisposeAll(epG);
}
}
if
(json)
{
foreach
(
var
r
in
results) Console.WriteLine(r.ToJson());
}
return
0
;
}
}
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3109271/ ,如需转载,请注明出处,否则将追究法律责任。