uint128_t Benchmarks

Methodology

Every cell below is the total time in microseconds that one operation takes over a whole vector of random values, so lower is better. The vector holds operands of random width (32, 64, 96, or 128 bits, e.g. 2x1 words, 1x2 words), each operation is evaluated as op(vec[i], vec[i + 1]) across the vector, and the whole vector is walked several times. Each platform states its element count and pass count, and therefore how many operations a cell covers. The Comparisons row is the total of all six relational operators (==, !=, <, , >, >=) over that same vector, which is why it costs roughly six arithmetic rows.

On most platforms we use the builtin unsigned __int128 as the reference benchmark. When this is unavailable (such as on 32-bit architectures) we use boost::multiprecision::uint128_t (abbreviated as boost::mp::uint128_t) as it is widely used, and known to be portable. On MSVC platforms we use as reference std::_Unsigned128 from the header <__msvc_int128.hpp> since this is bundled with their compiler. Lastly, when available, the benchmarks are also run with absl::uint128 which is the 128-bit unsigned integer from the Abseil Libraries.

Each platform reports a single run, several of them from shared CI runners, so treat small differences as noise.

Running the Benchmarks

The driver is test/benchmark_u128.cpp. From a Boost tree with its headers staged (b2 headers), build it in release mode and pass --json to have it write out a data set:

g++ -std=c++20 -O2 -DNDEBUG -I . libs/int128/test/benchmark_u128.cpp -o benchmark_u128
./benchmark_u128 --json u128.json

Timings are printed to stderr as they are measured, so a run is readable without the data set. --elements and --repetitions shrink the run, which is what the emulated platforms use.

From the root of a Boost tree, .github/scripts/run_benchmarks.sh --compiler g++ --out <dir> builds and runs both drivers exactly as CI does and leaves a complete data set folder behind. It takes its include path from the checked out libraries, so it needs no staged headers.

The benchmarks.yml workflow runs both drivers on every platform documented here and uploads one benchmarks-<os>-<arch> artifact per platform. It runs on every pull request, and each job’s summary compares its fresh numbers against the published ones, so a regression is visible without downloading anything. To refresh this page, unpack those artifacts into doc/modules/ROOT/data and run doc/render_benchmarks.py, which rewrites every table and plot below from the data sets.

Linux

x86_64

Measured with GCC 14.2 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).

Operation unsigned __int128 uint128_t boost::mp::uint128_t absl::uint128

Comparisons

538,116

545,110

1,100,158

504,183

Addition

66,230

60,477

65,991

60,608

Subtraction

65,810

71,192

65,943

64,127

Multiplication

86,476

74,446

86,230

74,626

Division

908,484

761,299

940,478

928,884

Modulo

837,475

697,322

858,246

856,182

x64 Relative Performance

ARM64

Measured with GCC 14.2 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).

Operation unsigned __int128 uint128_t boost::mp::uint128_t absl::uint128

Comparisons

1,551,148

954,612

2,090,864

1,559,081

Addition

88,531

87,204

87,303

87,544

Subtraction

89,194

90,112

90,873

87,986

Multiplication

86,854

87,632

88,376

91,189

Division

732,287

652,951

766,334

722,710

Modulo

768,287

662,783

801,508

758,006

ARM64 Relative Performance

S390x

Measured with GCC 12.2 (C++20): 5 passes over 2,000,000 element pairs (10,000,000 operations per cell).

Operation unsigned __int128 uint128_t boost::mp::uint128_t absl::uint128

Comparisons

609,693

539,203

1,218,003

1,178,669

Addition

50,814

44,087

50,939

50,573

Subtraction

50,584

41,277

50,738

53,373

Multiplication

53,604

36,614

50,442

106,646

Division

782,276

608,192

813,958

830,929

Modulo

812,511

607,793

840,997

874,632

s390x Relative Performance

PPC64LE

Measured with GCC 12.2 (C++20): 5 passes over 2,000,000 element pairs (10,000,000 operations per cell).

Operation unsigned __int128 uint128_t boost::mp::uint128_t

Comparisons

415,221

453,245

690,480

Addition

25,106

16,257

24,998

Subtraction

25,044

14,548

24,982

Multiplication

17,000

16,304

16,310

Division

581,682

489,550

589,757

Modulo

581,990

490,872

590,958

ppc64le Relative Performance

x86_32

This platform has no hardware type so we compare relative to boost::mp::uint128_t

Measured with GCC 14.2 (C++20): 5 passes over 10,000,000 element pairs (50,000,000 operations per cell).

Operation uint128_t boost::mp::uint128_t

Comparisons

2,112,319

1,568,536

Addition

143,659

1,144,222

Subtraction

156,320

1,382,478

Multiplication

203,481

1,221,882

Division

1,558,822

2,170,543

Modulo

1,526,280

1,873,501

x86 Relative Performance

ARM32

This platform has no hardware type so we compare relative to boost::mp::uint128_t

Measured with GCC 12.2 (C++20): 5 passes over 2,000,000 element pairs (10,000,000 operations per cell).

Operation uint128_t boost::mp::uint128_t

Comparisons

1,148,095

738,322

Addition

85,164

970,287

Subtraction

83,783

1,257,721

Multiplication

128,178

1,306,808

Division

3,858,870

6,092,878

Modulo

4,059,654

5,190,052

ARM32 Relative Performance

Windows

x86_64

Measured with MSVC 14.51 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).

Operation std::_Unsigned128 uint128_t boost::mp::uint128_t

Comparisons

1,313,048

1,251,724

1,970,110

Addition

106,229

98,075

2,153,287

Subtraction

115,275

97,252

2,318,242

Multiplication

115,091

112,082

2,428,936

Division

1,127,057

1,086,078

3,340,168

Modulo

1,249,176

1,144,617

2,721,519

x64 Relative Performance

ARM64

Measured with MSVC 14.44 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).

Operation std::_Unsigned128 uint128_t boost::mp::uint128_t

Comparisons

1,262,216

753,322

1,757,122

Addition

86,038

84,791

1,535,904

Subtraction

86,521

87,885

1,712,929

Multiplication

458,107

82,947

1,780,541

Division

1,334,323

920,483

2,843,970

Modulo

1,491,426

915,935

2,420,312

ARM64 Relative Performance

x86_32

Measured with MSVC 14.51 (C++20): 5 passes over 10,000,000 element pairs (50,000,000 operations per cell).

Operation std::_Unsigned128 uint128_t boost::mp::uint128_t

Comparisons

2,519,936

2,006,459

1,516,713

Addition

132,021

129,303

1,184,928

Subtraction

505,196

127,036

1,526,177

Multiplication

765,417

960,622

1,459,692

Division

2,510,903

2,217,091

2,816,842

Modulo

1,985,290

2,271,454

2,458,916

x86 Relative Performance

macOS

ARM64 (Apple Silicon)

Measured with Apple Clang 21.0 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).

Operation unsigned __int128 uint128_t boost::mp::uint128_t absl::uint128

Comparisons

363,881

351,327

403,043

373,414

Addition

67,797

58,792

64,948

53,003

Subtraction

49,226

45,231

63,076

67,021

Multiplication

68,952

54,470

64,614

59,419

Division

1,400,576

1,477,954

1,752,490

1,315,311

Modulo

1,376,647

1,462,446

2,062,026

1,672,512

ARM64 Relative Performance