Я разрабатывал проект C ++ из существующего кода Java. У меня есть следующий код C ++ и чтение кода Java из того же тестового файла, который состоит из миллионов целых чисел.
C ++:
int * arr = new int[len]; //len is larger than the largest int from the data
fill_n(arr, len, -1); //fill with -1
long loadFromIndex = 0;
struct stat sizeResults;
long size;
if (stat(fileSrc, &sizeResults) == 0) {
size = sizeResults.st_size; //here size would be ~551950000 for 552M test file
}
mmapFile = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, pageNum*pageSize);
long offset = loadFromIndex % pageSize;
while (offset < size) {
int i = htonl(*((int *)(mmapFile + offset)));
offset += sizeof(int);
int j = htonl(*((int *)(mmapFile + offset)));
offset += sizeof(int);
swapElem(i, j, arr);
}
return arr;
Джава:
IntBuffer bb = srcFile.getChannel()
.map(MapMode.READ_ONLY, loadFromIndex, size)
.asIntBuffer().asReadOnlyBuffer();
while (bb.hasRemaining()) {
int i = bb.get();
int j = bb.get();
swapElem(i, j, arr); //arr is an int[] of the same size as the arr in C++ version, filled with -1
}
return arr;
void swapElem(arr)
в C ++ и Java идентичны. Он сравнивает и изменяет значения в массиве, но оригинальный код довольно длинный для публикации здесь. В целях тестирования я заменил его следующей функцией, чтобы цикл не был мертвым кодом:
void swapElem(int i, int j, int * arr){ // int[] in Java
arr[i] = j;
}
Я предполагал, что версия C ++ должна превосходить версию Java, но тест дает противоположный результат — код Java почти в два раза быстрее, чем код C ++. Есть ли способ улучшить код C ++?
Я чувствую, может быть, mmapFile+offset
в C ++ повторяется слишком много раз, так что это O (n) дополнений для этого и O (n) дополнений для offset+=sizeof(int)
где n — количество целых чисел для чтения. Для Java IntBuffer.get()
, он просто непосредственно читает из индекса буфера, поэтому не требуется никакой операции сложения, кроме O (n) приращений индекса буфера на 1. Поэтому, включая приращения индекса буфера, C ++ принимает O (2n) дополнений, в то время как Java принимает O (n) ) дополнения. Когда речь идет о миллионах данных, это может привести к значительной разнице в производительности.
Следуя этой идее, я изменил код C ++ следующим образом:
mmapBin = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, pageNum*pageSize);
int len = size - loadFromIndex % pageSize;
char * offset = loadFromIndex % pageSize + mmapBin;
int index = 0;
while (index < len) {
int i = htonl(*((int *)(offset)));
offset += sizeof(int);
int j = htonl(*((int *)(offset)));
offset += sizeof(int);
index+=2*sizeof(int);
}
Я предполагал, что будет небольшой прирост производительности, но это не так.
Кто-нибудь может объяснить, почему код C ++ работает медленнее, чем код Java? Благодарю.
Я должен извиниться, что когда я сказал, что -O2 не работает, возникла проблема с моей стороны. Я испортил Makefile, поэтому код C ++ не перекомпилировался с использованием -O2. Я обновил производительность, и версия C ++ с использованием -O2 превзошла версию Java. Это может решить вопрос, но если кто-то захочет поделиться тем, как улучшить код C ++, я последую. Обычно я ожидаю, что это будет в 2 раза быстрее, чем код Java, но в настоящее время это не так. Спасибо всем за ваш вклад.
Компилятор: g ++
Флаги: -стена -c -O2
Версия Java: 1.8.0_05
Размер файла: 552 МБ, все 4-байтовые целые
Процессор: 2,53 ГГц Intel Core 2 Duo
Память 4 ГБ 1067 МГц DDR3
Время версии (мс)
C ++: ~ 1100
Ява: ~ 1400
C ++ (без цикла while): ~ 35
Java (без цикла while): ~ 40
У меня есть что-то перед этим кодом, которое вызывает производительность ~ 35 мс (в основном, заполнение массива -1), но это не важно здесь.
У меня есть некоторые сомнения в правильности эталонного метода. Оба кода являются «мертвыми» кодами. На самом деле вы нигде не используете i и j, поэтому компилятор gcc или Java JIT могут решить удалить цикл, видя, что он не влияет на будущий поток кода.
В любом случае, я бы изменил код C ++ на:
mmapFile = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, pageNum*pageSize);
long offset = loadFromIndex % pageSize;
int i, j;
int szInc = 2 * sizeof(int);
while (offset < size) {
scanf(mmapFile, "%d", &i);
scanf(mmapFile, "%d", &j);
offset += szInc; // offset += 8;
}
Это было бы эквивалентно коду Java. Кроме того, я бы продолжил использовать -O2 в качестве флагов компиляции. Имейте в виду, что htonl
это дополнительное преобразование, которое Java-код, похоже, не делает.