int128_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 __int128 as the reference benchmark.
When this is unavailable (such as on 32-bit architectures) we use boost::multiprecision::int128_t (abbreviated as boost::mp::int128_t) as it is widely used, and known to be portable.
On MSVC platforms we use as reference std::_Signed128 from the header <__msvc_int128.hpp> since this is bundled with their compiler.
Lastly, when available, the benchmarks are also run with absl::int128 which is the 128-bit signed 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_i128.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_i128.cpp -o benchmark_i128
./benchmark_i128 --json i128.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 | __int128 |
int128_t |
boost::mp::int128_t |
absl::int128 |
|---|---|---|---|---|
Comparisons |
575,065 |
500,451 |
1,703,153 |
503,204 |
Addition |
65,874 |
60,371 |
176,054 |
60,901 |
Subtraction |
65,944 |
72,082 |
569,596 |
71,309 |
Multiplication |
86,629 |
74,522 |
196,324 |
74,967 |
Division |
999,372 |
978,885 |
985,280 |
970,981 |
Modulo |
940,401 |
963,188 |
1,010,396 |
918,835 |
ARM64
Measured with GCC 14.2 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).
| Operation | __int128 |
int128_t |
boost::mp::int128_t |
absl::int128 |
|---|---|---|---|---|
Comparisons |
1,548,448 |
978,632 |
2,358,441 |
1,576,854 |
Addition |
86,459 |
87,101 |
221,898 |
88,399 |
Subtraction |
87,087 |
85,946 |
455,419 |
86,913 |
Multiplication |
84,919 |
84,091 |
187,422 |
83,956 |
Division |
814,967 |
767,734 |
966,484 |
812,755 |
Modulo |
839,380 |
767,831 |
953,283 |
841,903 |
S390x
Measured with GCC 12.2 (C++20): 5 passes over 2,000,000 element pairs (10,000,000 operations per cell).
| Operation | __int128 |
int128_t |
boost::mp::int128_t |
absl::int128 |
|---|---|---|---|---|
Comparisons |
785,777 |
708,911 |
2,059,404 |
525,305 |
Addition |
50,685 |
44,057 |
258,246 |
37,090 |
Subtraction |
50,668 |
41,357 |
303,965 |
41,352 |
Multiplication |
53,734 |
36,512 |
150,617 |
36,708 |
Division |
752,827 |
682,729 |
919,962 |
752,655 |
Modulo |
802,322 |
720,485 |
851,901 |
796,760 |
PPC64LE
Measured with GCC 12.2 (C++20): 5 passes over 2,000,000 element pairs (10,000,000 operations per cell).
| Operation | __int128 |
int128_t |
boost::mp::int128_t |
|---|---|---|---|
Comparisons |
428,645 |
462,487 |
1,021,098 |
Addition |
24,939 |
16,225 |
118,021 |
Subtraction |
104,630 |
14,546 |
172,685 |
Multiplication |
16,136 |
16,268 |
88,742 |
Division |
614,489 |
509,815 |
629,501 |
Modulo |
627,865 |
686,382 |
642,930 |
x86_32
This platform has no hardware type so we compare relative to boost::mp::int128_t
|
Measured with GCC 14.2 (C++20): 5 passes over 10,000,000 element pairs (50,000,000 operations per cell).
| Operation | int128_t |
boost::mp::int128_t |
|---|---|---|
Comparisons |
1,803,323 |
2,166,376 |
Addition |
159,907 |
1,198,244 |
Subtraction |
178,360 |
1,416,733 |
Multiplication |
213,890 |
1,726,368 |
Division |
1,711,915 |
2,680,645 |
Modulo |
1,706,134 |
2,539,888 |
ARM32
This platform has no hardware type so we compare relative to boost::mp::int128_t
|
Measured with GCC 12.2 (C++20): 5 passes over 2,000,000 element pairs (10,000,000 operations per cell).
| Operation | int128_t |
boost::mp::int128_t |
|---|---|---|
Comparisons |
1,199,464 |
1,149,576 |
Addition |
90,313 |
1,105,341 |
Subtraction |
83,826 |
1,065,408 |
Multiplication |
128,179 |
1,710,617 |
Division |
3,477,343 |
5,506,901 |
Modulo |
3,495,645 |
5,094,644 |
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::_Signed128 |
int128_t |
boost::mp::int128_t |
|---|---|---|---|
Comparisons |
1,361,518 |
1,271,189 |
4,315,263 |
Addition |
115,135 |
107,633 |
2,089,612 |
Subtraction |
108,885 |
105,990 |
2,265,849 |
Multiplication |
196,081 |
113,761 |
3,024,735 |
Division |
1,031,262 |
1,241,974 |
4,191,052 |
Modulo |
1,080,810 |
1,327,820 |
4,139,305 |
ARM64
Measured with MSVC 14.44 (C++20): 5 passes over 20,000,000 element pairs (100,000,000 operations per cell).
| Operation | std::_Signed128 |
int128_t |
boost::mp::int128_t |
|---|---|---|---|
Comparisons |
1,191,566 |
1,125,261 |
2,871,780 |
Addition |
86,160 |
84,055 |
1,598,093 |
Subtraction |
88,263 |
87,934 |
1,769,736 |
Multiplication |
505,934 |
81,566 |
2,395,038 |
Division |
1,506,403 |
1,480,602 |
3,622,476 |
Modulo |
1,722,778 |
1,447,734 |
3,555,076 |
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::_Signed128 |
int128_t |
boost::mp::int128_t |
|---|---|---|---|
Comparisons |
2,084,732 |
1,947,041 |
2,898,710 |
Addition |
131,344 |
128,063 |
1,325,852 |
Subtraction |
447,258 |
128,145 |
1,454,691 |
Multiplication |
877,318 |
149,394 |
2,018,981 |
Division |
3,479,471 |
3,113,092 |
3,948,298 |
Modulo |
2,683,535 |
3,157,295 |
3,620,409 |
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 | __int128 |
int128_t |
boost::mp::int128_t |
absl::int128 |
|---|---|---|---|---|
Comparisons |
479,219 |
358,275 |
1,035,078 |
405,457 |
Addition |
152,489 |
90,873 |
444,144 |
72,358 |
Subtraction |
51,306 |
75,991 |
383,429 |
87,675 |
Multiplication |
60,826 |
49,480 |
205,901 |
64,798 |
Division |
1,363,999 |
1,378,656 |
1,893,255 |
1,372,411 |
Modulo |
1,534,656 |
1,435,001 |
2,223,197 |
1,490,100 |