Int32 VS Int64 performance
Having compared int to float arithmetics, one should wonder: is there a similar difference between Int32 and Int64? Let’s find out!
Benchmarks
As usually, we are going to get to the numbers right away
:
AdditionError Units
Benchmark Mode Cnt Score .sumInt32 avgt 10 220.515 ± 0.365 us/op
Int32vs64.sumInt64 avgt 10 224.636 ± 0.771 us/op
Int32vs64
:
SubtractionError Units
Benchmark Mode Cnt Score .minusInt32 avgt 10 219.931 ± 0.314 us/op
Int32vs64.minusInt64 avgt 10 223.050 ± 1.612 us/op
Int32vs64
:
MultiplicationError Units
Benchmark Mode Cnt Score .multiplyInt32 avgt 10 654.286 ± 1.963 us/op
Int32vs64.multiplyInt64 avgt 10 649.246 ± 1.391 us/op
Int32vs64
:
DivisionError Units
Benchmark Mode Cnt Score .divideInt32 avgt 10 1971.968 ± 3.969 us/op
Int32vs64.divideInt64 avgt 10 1945.585 ± 4.307 us/op Int32vs64
Benchmarks made with JMH. Code below.
CPU: AMD Ryzen 9 5900x
OS: Ubuntu 21.10 64-bit
JVM: OpenJDK 17 64-bit
JMH: version 1.34
Score: how long does it take to execute an operation on 1 mln values
Conclusions
As we can see, there is no measurable difference in arithmetic performance between int and long data types. The reason for that is fairly simple: most modern systems use 64-bit registers for both types. Historically, that wasn’t the case and int used to represent different sizes depending on CPU and ranged from 4 bits (Intel 4004) to 64 bits (IBM 7030). In fact, it comes down to a notion of a word. Int usually represents a single word, while long represents double word.
A word - is a number of bits for majority of the CPU registers. Intel 8080 used to have 8 bits per register. All modern CPU architectures (x86-64, ARMv8, ARMv9, z/Architecture, etc) are using 64-bit words. You may still encounter some 32-bit CPUs, either in older servers or embedded devices space, using older architectures like ARMv7, ARMv6 (Raspberry Pi, Apple M7), IA-64 (Itanium servers), PowerPC.
Another important factor to consider is OS data model. Popular data models include: LP64, LLP64 and ILP64.
Model | short | int | long | long long | pointer | OS | Desc |
---|---|---|---|---|---|---|---|
ILP32 | 16 | 32 | 32 | 64 | 32 | Embedded systems | Int, Long, Pointer, 32 |
LLP64 | 16 | 32 | 32 | 64 | 64 | Windows | Long Long, Pointer, 64 |
LP64 | 16 | 32 | 64 | 64 | 64 | Linux, MacOS, BSD, Solaris, z/OS, Windows + Cygwin | Long, Pointer, 64 |
ILP64 | 16 | 64 | 64 | 64 | 64 | HAL Computer Systems | Int, Long, Pointer, 64 |
SILP64 | 64 | 64 | 64 | 64 | 64 | UNICOS | Short, Int, Long, Pointer, 64 |
As you can see, the vast majority of modern CPUs (both ARM and x86) use 64-bit registers for both int and long.
To ensure better compatibility, most compilers set the size of an integer and long according to their spec instead of relying on OS data model. For example, C and C++ compilers define int32_t and int64_t types in <stdint.h>. You may need to check specification of your compiler.
Pointer length can also differ between compilers and even compiler settings. For example, using parameter UseCompressedOops passed to JVM you are going to enable compressed pointers, that take only 32-bits instead of 64. In fact, JVM can be configured to work in ILP32, LP64 or ILP64 mode.
Code:
Sources
Intel:
Using the ILP64 Interface vs. LP64 Interface
UNIX: 64-Bit
Programming Models: Why LP64?
IBM:
LP64 application performance and program size
Wikipedia:
64-bit computing
Wikipedia:
x86 instruction listings
OpenJDK
Wiki: CompressedOops by John Rose
05 Apr 2022 - Hasan Al-Ammori