I hear from colleagues that C++ is faster than Java and when looking for top performance, especially for finance applications, that's the route to go. But my observations differ a bit. Can anyone point out failures on my experiment or add some scientific variables to the discussion?
Note1: I am using -O3 (maximum optimization) and -O2 with the C++ compiler.
Note2: The short and simple complete source codes for each language are included. Feel free to run on your own machine, make changes, draw conclusions and share.
Note3: If you put both source codes side by side in an editor, you will see that their implementations are equivalent.
UPDATE: I've tried clang++
and g++
with a variety of optimization options (-O2
, -O3
, -Os
, -march=native
, etc) and they all have produced slower results than Java. I think at this point to make C++ faster I have to dive into the generated assembly code and do some assembly programming. I'm wondering how practical is this approach (assembly programming and assembly debugging) when coding a large real-life application.
What does the benchmark do?
- Create an int array in the heap (not in the stack)
- Start the clock
- Populate the array
- Sort the array with bubble sort
- Stop the clock
Do that 10 million times, discard the first 1 million for warming up and output the average, min and max time.
For C++ I get: (with -O3 and -O2)
$ g++ --version
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
$ g++ TimeBubbleSort.cpp -o TimeBubbleSort -std=c++11 -O3
$ ./TimeBubbleSort 10000000 1000000 60
Value computed: 18300000000
Iterations: 9000000 | Avg Time: 1202 | Min Time: 1158 | Max Time: 212189
$ g++ TimeBubbleSort.cpp -o TimeBubbleSort -std=c++11 -O2
$ ./TimeBubbleSort 10000000 1000000 60
Value computed: 18300000000
Iterations: 9000000 | Avg Time: 1337 | Min Time: 1307 | Max Time: 36650
For Java I get:
$ java -version
java version "17.0.1" 2021-10-19 LTS
Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-39)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-39, mixed mode, sharing)
$ javac -cp . TimeBubbleSort.java
$ java -cp . TimeBubbleSort 10000000 1000000 60
Value computed: 18300000000
Iterations: 9000000 | Avg Time: 837.0 | Min Time: 812 | Max Time: 37196
Full C++ code:
#include <iostream>
#include <limits>
#include <sstream>
using namespace std;
// TO COMPILE: g++ TimeBubbleSort.cpp -o TimeBubbleSort -std=c++11 -O3
// TO EXECUTE: ./TimeBubbleSort 10000000 1000000 60
long get_nano_ts(timespec* ts) {
clock_gettime(CLOCK_MONOTONIC, ts);
return ts->tv_sec * 1000000000 + ts->tv_nsec;
}
struct mi {
long value;
};
void swapping(int &a, int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
void bubbleSort(int *array, int size) {
for(int i = 0; i < size; i++) {
bool swaps = false;
for(int j = 0; j < size - i - 1; j++) {
if(array[j] > array[j+1]) {
swapping(array[j], array[j+1]);
swaps = true;
}
}
if (!swaps) break;
}
}
void doSomething(int *array, int size) {
for(int z = 0; z < size; z++) {
array[z] = size - z;
}
bubbleSort(array, size);
}
int main(int argc, char* argv[]) {
int iterations = stoi(argv[1]);
int warmup = stoi(argv[2]);
int arraySize = stoi(argv[3]);
struct timespec ts;
long long x = 0;
long long totalTime = 0;
int minTime = numeric_limits<int>::max();
int maxTime = numeric_limits<int>::min();
int * array = (int*) malloc(arraySize * sizeof(int));
for(int i = 0; i < iterations; i++) {
long start = get_nano_ts(&ts);
doSomething(array, arraySize);
long end = get_nano_ts(&ts);
for(int j = 0; j < arraySize; j++) {
x += array[j];
}
int res = end - start;
if (res <= 0) res = 1;
if (i >= warmup) {
totalTime += res;
minTime = min(minTime, res);
maxTime = max(maxTime, res);
}
}
int count = iterations - warmup;
double avg = totalTime / count;
cout << "Value computed: " << x << endl;
stringstream ss;
ss << "Iterations: " << count << " | Avg Time: " << avg;
if (count > 0) {
ss << " | Min Time: " << minTime << " | Max Time: " << maxTime;
}
cout << ss.str() << endl << endl;
free(array);
return 0;
}
Full Java code:
public class TimeBubbleSort {
// javac -cp . TimeBubbleSort.java
// java -cp . TimeBubbleSort 10000000 1000000 60
private static void swapping(int[] array, int x, int y) {
int temp = array[x];
array[x] = array[y];
array[y] = temp;
}
private static void bubbleSort(int[] array, int size) {
for(int i = 0; i < size; i++) {
int swaps = 0; // flag to detect any swap is there or not
for(int j = 0; j < size - i - 1; j++) {
if (array[j] > array[j + 1]) { // when the current item is bigger than next
swapping(array, j, j + 1);
swaps = 1;
}
}
if (swaps == 0) break; // No swap in this pass, so array is sorted
}
}
private final static void doSomething(int[] array, int size) {
for(int z = 0; z < size; z++) {
array[z] = size - z;
}
bubbleSort(array, size);
}
public static void main(String[] args) {
int iterations = Integer.parseInt(args[0]);
int warmup = Integer.parseInt(args[1]);
int arraySize = Integer.parseInt(args[2]);
long x = 0;
long totalTime = 0;
long minTime = Long.MAX_VALUE;
long maxTime = Long.MIN_VALUE;
int[] array = new int[arraySize];
for(int i = 0; i < iterations; i++) {
long start = System.nanoTime();
doSomething(array, arraySize);
long end = System.nanoTime();
for(int j = 0; j < arraySize; j++) {
x += array[j];
}
int res = (int) (end - start);
if (res <= 0) res = 1;
if (i >= warmup) {
totalTime += res;
minTime = Math.min(minTime, res);
maxTime = Math.max(maxTime, res);
}
}
int count = iterations - warmup;
double avg = totalTime / count;
StringBuilder sb = new StringBuilder();
sb.append("Value computed: ").append(x).append("\n");
sb.append("Iterations: ").append(count).append(" | Avg Time: ").append(avg);
if (count > 0) {
sb.append(" | Min Time: ").append(minTime).append(" | Max Time: ").append(maxTime);
}
System.out.println(sb.toString() + "\n");
}
}