Отличие в скорости работы
Написал вот дакой код. Если вместо vector<char> выбрать тип vector<int>, то скорость выполнения программы увеличивается раза в 2. В чём может быть причина?
#include <iostream>
#include <vector>
int main() {
std::cout << "Hello, World!" << std::endl;
std::vector<int> v;
for (auto i = 1; i <= 500000000; i++) {
v.push_back(i);
if (i % 100000000 == 0) {
std::cout << i << std::endl;
}
}
return 0;
}
Ответы (1 шт):
Все не так как кажется на первый взгляд. То, что для программиста выглядит очень похожим, на самом деле в коде может быть совершенно разным. Совершенно.
Давайте посмотрим на внутренний цикл с точки зрения gcc с -O0.
c int
.L5:
cmp DWORD PTR -52[rbp], 500000000
jg .L4
mov eax, DWORD PTR -52[rbp]
mov BYTE PTR -53[rbp], al
lea rdx, -53[rbp]
lea rax, -48[rbp]
mov rsi, rdx
mov rdi, rax
call std::vector<char, std::allocator<char> >::push_back(char&&)
add DWORD PTR -52[rbp], 1
jmp .L5
.L4:
char
.L5:
mov eax, DWORD PTR -52[rbp]
cmp eax, 500000000
jg .L4
lea rdx, -52[rbp]
lea rax, -48[rbp]
mov rsi, rdx
mov rdi, rax
call std::vector<int, std::allocator<int> >::push_back(int const&)
mov eax, DWORD PTR -52[rbp]
add eax, 1
mov DWORD PTR -52[rbp], eax
jmp .L5
.L4:
Компилятор как минимум сделал больше кода. К примеру, в случае с int он просто инкрементит переменную по адресу в памяти. В случае с char - вычитывает с памяти в регистр, увеличивает и пишет назад. То же самое и с сравнением. А чтение-запись памяти дешево не дается. Это объясняет, почему вариант с char медленее. Добавим -O1 и картина сильно меняется. В моих тестах char быстрее где то в 2.5 раза. Но и общее время сильно-сильно улучшилось
Я сделал такое же и для clang. Результат не сильно отличается. Правда clang сделал более компактный код. Сравним.
для int
.LBB0_1: # =>This Inner Loop Header: Depth=1
cmp dword ptr [rbp - 36], 500000000
jg .LBB0_6
lea rdi, [rbp - 32]
lea rsi, [rbp - 36]
call std::vector<int, std::allocator<int> >::push_back(int const&)
jmp .LBB0_3
.LBB0_3:
char
.LBB0_1: # =>This Inner Loop Header: Depth=1
cmp dword ptr [rbp - 36], 500000000
jg .LBB0_6
mov al, byte ptr [rbp - 36]
mov byte ptr [rbp - 37], al
lea rdi, [rbp - 32]
lea rsi, [rbp - 37]
call std::vector<char, std::allocator<char> >::push_back(char&&)
jmp .LBB0_3
.LBB0_3:
Но вот с O1 clang был медленее gcc c O1. Но тесты для char/int дали приблизительно одинаковые результаты. Но уже на O2 все стало на свои места.
Вывод. Такая разница не в выводе на консоль. пять маленьких строк не сыграют большой роли при 2-3 секундах выполнения.
Компилятор не спроста сделал так. Все дело в разных типах. Компилятор вынужден вставить неявное преобразование int в char. Для этого он копирует только последний байт регистра. А так как регистр eax при этом "портится", то его нужно постоянно куда то сохранять. Вот и весь ответ.