div.main {margin-left: 20pt; margin-right: 20pt}Java против C. Версия Ru.Java.
Что быстрее C или Java с ее многочисленными ухищрениями, JIT-ами, HotSpot-ами
и т.д. ? Что быстрее байткод или нативный уже откомпилированный под целевую
платформу код?
Такими вопросами задались однажды подписчики телеконференции иерархии ФИДО
Ru.Java летом 2000 года. Задались и решили проверить, а всегда ли нативный код
быстрее (умнее, сильнее, выше и дальше :-) байткода выполняемого вирутальной
машиной Java. Для этого были сформированы тестовые задачи и были проведены
тесты. Как оказалось C даже не всегда вырывала победу и своего соперника.
Итак все по порядку.
Любому новичку будет ясно, что скомпилированный нативный код будет работать
всегда быстрее "интерпретируемого" байткода, ведь последний должен еще и
"переводиться" на понятный машине язык (без перевода он понятен только
виртуальной машине). Но в действительности не все так как на самом деле, не
будем вдаваться в технологические дебри реализаций и моделей вирутальных машин,
а просто вспомним, что уже достаточно большое время в виртуальных машинах Java
используются технологии JIT, а в некоторых даже и патентованные средства с названием
HotSpot. Что же они делают? Из-за чего такой прирост
производительности?
Эти технологии в общих чертах позволяют переводить интерпретируемый код для
виртуальной машины в оптимизированный компилированный код целевой платформы. В
некоторых реализациях эта процедура производиться не для всего кода, а только
для тех критических участков, которые обычно и становятся "бутылочными
горлышками" в производительности. Тем самым поднимается производительность и
может экономиться время на компиляцию. Фокус так же заключается и в том, что
производиться оптимизация относительно текущих параметров системы (ведь
производиться компиляция во время выполнения, как говориться на лету) в отличии
от компиляторов C, где код компилируется один раз и заранее нельзя угадать где и
как он будет исполняться.
Чем проводилось тестирование:
Тесты Java:
Ack.java -
функция Аккермана Ack2.java -
функция Аккермана Iter.java -
простая итерация Iter2.java -
простая итерация с оптимизацией для HotSpot Server. Iter3.java -
простая итерация с увеличенными в два раза "матрицами" Sort.java - N^2
сортировка Sort2.java - N^2 сортировка
c увеличенной в два раза "матрицей" Linpack benchmark - стандаpтный тест пpоизводительности на опеpациях с линейной
алгебpой
Тесты C:
ACK.CPP -
функция Аккермана ITER.CPP -
простая итерация ITER3.cpp - простая итерация с
увеличенными в два раза "матрицами" SORT.CPP - N^2 сортировка SORT2.CPP - N^2 сортировка
c увеличенной в два раза "матрицей" Linpack benchmark - стандаpтный тест пpоизводительности на опеpациях с линейной
алгебpой
Тесты Дельфи:
ITER.PAS
- простая итерация. Существует, так же второй вариант, несколько более
быстрый ITER2.PAS.
Тесты "Паскаль":
Ack.pas - функция Аккермана
Результаты (цветом в таблице отделены различные
испытания):
Компилятор/VM |
Время выполнения в секундах (чем меньше, тем лучше)
|
Тест |
Комментарии |
gcc 2.95.2 |
5.037 |
ack.cpp |
|
msvc 6 sp 3 |
4.085 |
ack.cpp |
sun jdk1.2.2 |
5.618 |
Ack.java |
sun jdk1.2.2 |
9.684 |
Ack2.java |
gcc 2.95.2 |
2.974 |
iter.cpp |
msvc 6 sp 3 |
3.565 |
iter.cpp |
sun jdk1.2.2 |
3.595 |
Iter.java |
Sun 1.2.2 |
6.31 |
Iter.java |
Celeron 433 64M Win98 |
IBM 1.1.8 |
5.31 |
Iter.java |
jview 5.00.3031 |
7,29 |
Iter.java |
Cygnus (cygwin-b20) egcs-2.91.57 |
12,183 |
Iter.cpp |
Cygnus (cygwin-b20) egcs-2.91.57 -O2 |
6,317 |
Iter.cpp |
Delphi |
6,302 |
Iter.pas |
gcc |
6.5 |
Iter.cpp |
|
msvc |
6.4 |
Iter.cpp |
bcc32 -O2 (Borland C++ 5.4) |
7.1 |
Iter.cpp |
bcc32 -O2 (Borland C++ 5.0) |
7.2 |
Iter.cpp |
bcc32i -O2 (Borland C++ 5.0) |
6.7 |
Iter.cpp |
MS jview (5.00.3240) |
8.4 |
Iter.java |
IBM jre (1.1.8) |
6.1 |
Iter.java |
sun jdk 1.3.0 (HotSpot Client) |
11.9 |
Iter.java |
sun jdk 1.3.0 (HotSpot Server 2.0) |
16.4 |
Iter.java |
sun jdk 1.3.0 (HotSpot Server 2.0) |
8.642 |
Iter2.java |
gcc 2.95.2 |
3.0 |
Sort.cpp |
|
msvc 6 sp 3 |
3.7 |
Sort.cpp |
sun jdk1.2.2 |
5.1 |
Sort.java |
gcc |
3.7 |
Sort.cpp |
|
msvc |
4.5 |
Sort.cpp |
bcc32 -O2 (Borland C++ 5.4) |
5.2 |
Sort.cpp |
bcc32 -O2 (Borland C++ 5.0) |
3.8 |
Sort.cpp |
bcc32i -O2 (Borland C++ 5.0) |
4.4 |
Sort.cpp |
MS jview (5.00.3240) |
7.7 |
Sort.java |
IBM jre (1.1.8) |
5.6 |
Sort.java |
sun jdk 1.3.0 (HotSpot Client) |
10.8 |
Sort.java |
sun jdk 1.3.0 (HotSpot Server 2.0) |
5.6 |
Sort.java |
gcc egcs-2.91.66 -O3 |
17.02 |
Ack.cpp |
Celeron-266(w/o L2 cache) linux-2.2.15 |
J2RE 1.3.0 IBM |
5.015 |
Ack.java |
J2RE 1.3.0 IBM |
6.129 |
Ack2.java |
gcc egcs-2.91.66 -O3 |
17.83 |
Iter.cpp |
J2RE 1.3.0 IBM |
17.028 |
Iter.java |
gcc -O3 -DCPU=686 |
6.61 |
Ack.cpp |
gcc -O3 -DCPU=686 |
17.97 |
Iter.cpp |
gcc -O3 -DCPU=686 |
8.11 |
Sort.cpp |
J2RE 1.3.0 IBM |
5.705 |
Sort.java |
gcc -O3 -DCPU=686 |
3.78 |
Ack.cpp |
Celeron 466 IntelZX |
J2RE 1.3.0 IBM |
2.862 |
Ack.java |
J2RE 1.3.0 IBM |
3.496 |
Ack2.java |
gcc -O3 -DCPU=686 |
5 |
Iter.cpp |
J2RE 1.3.0 IBM |
2.858 |
Iter.java |
gcc -O3 -DCPU=686 |
38.22 |
Iter3.cpp |
J2RE 1.3.0 IBM |
39.795 |
Iter3.java |
gcc -O3 -DCPU=686 |
4.64 |
Sort2.cpp |
J2RE 1.3.0 IBM |
3.254 |
Sort2.java |
gcc |
6.0 |
Ack.cpp |
|
msvc |
4.9 |
Ack.cpp |
bcc32 -O2 (Borland C++ 5.4) |
13.8 |
Ack.cpp |
bcc32 -O2 (Borland C++ 5.0) |
13.5 |
Ack.cpp |
bcc32i -O2 (Borland C++ 5.0) |
4.9 |
Ack.cpp |
MS jview (5.00.3240) |
6.0 |
Ack.java |
MS jview (5.00.3240) |
13.3 |
Ack2.java |
IBM jre (1.1.8) |
8.7 |
Ack.java |
IBM jre (1.1.8) |
14.6 |
Ack2.java |
sun jdk 1.3.0 (HotSpot Client) |
12.7 |
Ack.java |
sun jdk 1.3.0 (HotSpot Client) |
15.1 |
Ack2.java |
sun jdk 1.3.0 (HotSpot Server 2.0) |
5.2 |
Ack.java |
sun jdk 1.3.0 (HotSpot Server 2.0) |
5.3 |
Ack2.java |
IBM CSet for OS/2 3.6.5 Fix#1, |
4.523 |
Iter.cpp |
|
IBM VAC++ for OS/2 4.0 Fix#1 |
4.57 |
Iter.cpp |
JDK 1.1.8 IBM OS/2 |
4.219 |
Iter.java |
IBM CSet for OS/2 3.6.5 Fix#1, |
4.523 |
Ack.cpp |
IBM VAC++ for OS/2 4.0 Fix#1 |
2.914 |
Ack.cpp |
JDK 1.1.8 IBM OS/2 |
5.789 |
Ack.java |
JDK 1.1.8 IBM OS/2 |
8.946 |
Ack2.java |
sun jdk1.1.8 |
9.45 |
Ack.java |
|
TP7.1 |
46 |
Ack.pas |
TMTPascal 3.10 WStub0.6 |
31.58 |
Ack.pas |
VAC++ 4.0 Fix#1 for OS/2 |
3.344 |
Sort.cpp |
|
JDK 1.1.8 IBM OS/2 |
3.742 |
Sort.java |
Общий смысл вроде бы как ясен, с хорошими VM и операционными системами в
частности Java идет на равне с лучшими компиляторами C, а иногда даже выигрывает
в производительности. Однако на данный момент все-таки это тесты именно на
вычисления, работы с массивами и т.д. В жизни такие приложения встречаются реже,
чем ворочащие окошки с менюшками.
Компилятор/VM |
Производительность в Mflops |
Размер матрицы |
Комментарий |
GCJ 2.95.1 (native) |
15 |
100 |
RH Linux 6.0 |
IBM JDK-1.1.8 |
7.9 |
IBM JDK-1.3 |
3.9 |
IBM JDK-1.1.8 nojit |
1.6 |
GCJ 2.95.1 (native) |
15 |
200 |
IBM JDK-1.1.8 |
15 |
IBM JDK-1.3 |
13 |
Fortran |
35(справочная величина) |
100 |
Т. к. в этом бенчмаpке ничего не пpоисходит кpоме pазмещения массивов и
последyющих матpичных опеpаций, то вывод ясен: jit-compiler'ы пpоигpывают
native-кодy именно на вpемени стаpта и, возможно, вpемени pазмещения пеpеменных.
Некотоpое влияние на пpоизводительность оказывает pазмещение пеpеменных как
static (в данном слyчае совеpшенно все pавно, объявлять ли пеpеменные и методы
как пpинадлежащие к классy или объектy), опции оптимизации, использование
байт-кода, выданного "чyжим" компилятоpом. Эти yсловия, однако, не меняют общей
тенденции, котоpyю видно в таблице.
К сожалению, не все участники указали, конкретно, какая платформа
использовалась, какие процессоры, сколько памяти и т.д. Поэтому следует
сравнивать результаты, разделенные на группы, относительно друг друга.
|