привет, пользователи stackoverflow, это мой первый вопрос, поэтому, если у меня есть какие-либо ошибки в моем выражении, пожалуйста, укажите это, спасибо
Я написал эту простую функцию вычисления на Java и C ++
Джава:
long start = System.nanoTime();
long total = 0;
for (int i = 0; i < 2147483647; i++) {
total += i;
}
System.out.println(total);
System.out.println(System.nanoTime() - start);
C ++:
auto start = chrono::high_resolution_clock::now();
register long long total = 0;
for (register int i = 0; i < 2147483647; i++)
{
total += i;
}
cout << total << endl;
auto finish = chrono::high_resolution_clock::now();
cout << chrono::duration_cast<chrono::nanoseconds>(finish - start).count() << endl;
программного обеспечения:
— JDK8u11
— Microsoft Visual C ++ Compiler (2013)
Результаты:
Джава:
2305843005992468481
1096361110
C ++:
2305843005992468481
6544374300
Результаты расчета одинаковы, что хорошо
однако напечатанное время nano показывает, что Java-программе требуется 1 секунда, в то время как в C ++ для ее выполнения требуется 6 секунд.
Я давно занимаюсь Java, но я новичок в C ++, есть ли проблемы в моем коде? или это факт, что C ++ медленнее, чем Java с простыми вычислениями?
Кроме того, я использовал ключевое слово «register» в своем коде на C ++, надеясь, что это принесет улучшение производительности, но время выполнения совсем не отличается, может кто-нибудь объяснить это?
РЕДАКТИРОВАТЬ: Моя ошибка здесь заключается в том, что настройки компилятора C ++ не оптимизированы, и вывод устанавливается на x32, после применения / O2 WIN64 и удаления DEBUG, выполнение программы заняло всего 0,7 секунды
JDK по умолчанию применяет оптимизацию к выводу, однако это не относится к VC ++, который по умолчанию предпочитает скорость компиляции, разные компиляторы C ++ также различаются по результату, некоторые вычисляют результат цикла во время компиляции, что приводит к чрезвычайно короткому времени выполнения ( около 5 микросекунд)
ПРИМЕЧАНИЕ. При правильных условиях программа C ++ будет работать лучше, чем Java, в этом простом тесте, однако я заметил, что многие проверки безопасности во время выполнения пропускаются, нарушая намерение отладки как «безопасный язык», и я считаю, что C ++ будет еще превосходить Java в тест большого массива, так как он не имеет проверки границ
В Linux / Debian / Sid / x86-64, используя OpenJDK 7 с
// file test.java
class Test {
public static void main(String[] args) {
long start = System.nanoTime();
long total = 0;
for (int i = 0; i < 2147483647; i++) {
total += i;
}
System.out.println(total);
System.out.println(System.nanoTime() - start);
}
}
и GCC 4.9 с
// file test.cc
#include <iostream>
#include <chrono>
int main (int argc, char**argv) {
using namespace std;
auto start = chrono::high_resolution_clock::now();
long long total = 0;
for (int i = 0; i < 2147483647; i++)
{
total += i;
}
cout << total << endl;
auto finish = chrono::high_resolution_clock::now();
cout << chrono::duration_cast<chrono::nanoseconds>(finish - start).count()
<< endl;
}
Затем компилирование и запуск test.java
с
javac test.java
java Test
Я получаю вывод
2305843005992468481
774937152
при компиляции test.cc
с оптимизацией
g++ -O2 -std=c++11 test.cc -o test-gcc
и работает ./test-gcc
это идет намного быстрее
2305843005992468481
40291
Конечно без оптимизаций g++ -std=c++11 test.cc -o test-gcc
бег медленнее
2305843005992468481
5208949116
Глядя на ассемблерный код, используя g++ -O2 -fverbose-asm -S -std=c++11 test.cc
Я вижу, что компилятор вычислил результат во время компиляции:
.globl main
.type main, @function
main:
.LFB1530:
.cfi_startproc
pushq %rbx #
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
call _ZNSt6chrono3_V212system_clock3nowEv #
movabsq $2305843005992468481, %rsi #,
movl $_ZSt4cout, %edi #,
movq %rax, %rbx #, start
call _ZNSo9_M_insertIxEERSoT_ #
movq %rax, %rdi # D.35007,
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ #
call _ZNSt6chrono3_V212system_clock3nowEv #
subq %rbx, %rax # start, D.35008
movl $_ZSt4cout, %edi #,
movq %rax, %rsi # D.35008, D.35008
call _ZNSo9_M_insertIlEERSoT_ #
movq %rax, %rdi # D.35007,
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ #
xorl %eax, %eax #
popq %rbx #
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE1530:
.size main, .-main
Так что вам просто нужно включить оптимизацию в вашем компиляторе (или переключиться на лучший компилятор, как НКУ 4,9)
Кстати, на Java оптимизации низкого уровня происходят в JIT из JVM. Я плохо знаю JAVA, но не думаю, что мне нужно их включать. Я знаю, что в GCC вы должны включить оптимизацию, которая, конечно, досрочно (например, с -O2
)
PS: Я никогда не использовал компилятор Microsoft в этом 21-м веке, поэтому я не могу помочь вам в том, как включить в него оптимизацию.
Наконец, я не верю, что такие микробенчмарки значимы. Затем вы сможете оптимизировать ваши реальные приложения.
Занимает около 0,6 секунды (.592801000 секунд) в моей системе, Intel 2600K, 3,40 ГГц, с MSVC Express 2013, 64-разрядный режим, стандартная сборка выпуска. После установки финиша cout перемещен, чтобы не включать накладные расходы cout.
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
auto start = chrono::high_resolution_clock::now();
register long long total = 0;
for (register int i = 0; i < 2147483647; i++)
{
total += i;
}
auto finish = chrono::high_resolution_clock::now();
cout << total << endl;
cout << chrono::duration_cast<chrono::nanoseconds>(finish - start).count() << endl;
return 0;
}
Я думаю, что самый простой способ описать, почему C / C ++ ВСЕГДА будет быстрее, чем Java, это понять, как работает Java.
С самого начала Java была разработана для облегчения кроссплатформенного программного обеспечения. До Java нужно было скомпилировать свою программу на каждом семействе машин отдельно. Даже сейчас, с разнообразием аппаратных архитектур, принятых стандартов и ОС, никто не может обойти это препятствие. Java выполняет это с помощью своего компилятора и JVM. Компилятор применяет любую возможную оптимизацию и собирает ее в байт-код Java, что похоже на сокращенный оптимизированный источник, который был скомпилирован. Однако этот байт-код еще не может быть понят процессором.
Вот где появляется виртуальная машина Java. Сначала JVM выясняет, в какой среде она запускается, и загружает соответствующую таблицу перевода. Затем байт-код считывается в JVM, и каждый код ищется в таблице и транслируется в машинный код среды, а затем выполняется.
Как вы знаете, все это занимает немного времени на одну инструкцию. Но в случае скомпилированной программы на C / C ++ она уже находится в правильном машинном коде и выполняется немедленно.
Интересное примечание. Все операционные системы и большинство драйверов устройств написаны на языке C по соображениям производительности.