Я пытаюсь оценить, насколько быстро Java может выполнить простую задачу: прочитать огромный файл в память, а затем выполнить какие-то бессмысленные вычисления с данными. Учитываются все типы оптимизации. Будь то переписывание кода по-другому или использование другой JVM, обман JIT..
Входной файл представляет собой список длиной 500 миллионов пар 32-битных целых чисел, разделенных запятой. Так:
44439,5023
33140,22257
...
Этот файл занимает 5,5 ГБ на моем компьютере. Программа не может использовать более 8 ГБ оперативной памяти и может использовать только один поток.
package speedracer;
import java.io.FileInputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class Main
{
public static void main(String[] args)
{
int[] list = new int[1000000000];
long start1 = System.nanoTime();
parse(list);
long end1 = System.nanoTime();
System.out.println("Parsing took: " + (end1 - start1) / 1000000000.0);
int rs = 0;
long start2 = System.nanoTime();
for (int k = 0; k < list.length; k++) {
rs = calc(list[k++], list[k++], list[k++], list[k]);
}
long end2 = System.nanoTime();
System.out.println(rs);
System.out.println("Calculations took: " + (end2 - start2) / 1000000000.0);
}
public static int calc(final int a1, final int a2, final int b1, final int b2)
{
int c1 = (a1 + a2) ^ a2;
int c2 = (b1 - b2) << 4;
for (int z = 0; z < 100; z++) {
c1 ^= z + c2;
}
return c1;
}
public static void parse(int[] list)
{
FileChannel fc = null;
int i = 0;
MappedByteBuffer byteBuffer;
try {
fc = new FileInputStream("in.txt").getChannel();
long size = fc.size();
long allocated = 0;
long allocate = 0;
while (size > allocated) {
if ((size - allocated) > Integer.MAX_VALUE) {
allocate = Integer.MAX_VALUE;
} else {
allocate = size - allocated;
}
byteBuffer = fc.map(FileChannel.MapMode.READ_ONLY, allocated, allocate);
byteBuffer.clear();
allocated += allocate;
int number = 0;
while (byteBuffer.hasRemaining()) {
char val = (char) byteBuffer.get();
if (val == '\n' || val == ',') {
list[i] = number;
number = 0;
i++;
} else {
number = number * 10 + (val - '0');
}
}
}
fc.close();
} catch (Exception e) {
System.err.println("Parsing error: " + e);
}
}
}
Я перепробовал все, что мог придумать. Пробовал разные читалки, пробовал openjdk6, sunjdk6, sunjdk7. Пробовал разные читалки. Пришлось сделать какой-то уродливый синтаксический анализ, поскольку MappedByteBuffer не может одновременно отображать более 2 ГБ памяти. Я бегу:
Linux AS292 2.6.38-11-generic #48-Ubuntu SMP
Fri Jul 29 19:02:55 UTC 2011
x86_64 GNU/Linux. Ubuntu 11.04.
CPU: is Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz.
В настоящее время мои результаты для разбора: 26,50 с, расчеты: 11,27 с. Я конкурирую с аналогичным тестом C++, который выполняет ввод-вывод примерно за то же время, но вычисления занимают всего 4,5 секунды. Моя главная цель - сократить время расчета любыми возможными способами. Есть идеи?
Обновление: кажется, что основное улучшение скорости может быть связано с тем, что называется Автоматическая векторизация. Мне удалось найти некоторые намеки на то, что текущий JIT Sun выполняет только «некоторую векторизацию», однако я не могу это подтвердить. Было бы здорово найти какую-нибудь JVM или JIT с лучшей поддержкой оптимизации автовекторизации.
g++ -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.cpp" g++ -o "perf" ./main.o -lboost_program_options
версии компилятораg++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
. Насколько мне известно, SSE2 не использовался. - person Zilvinas   schedule 17.09.2011