Отличие в скорости работы

Написал вот дакой код. Если вместо 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 шт):

Автор решения: KoVadim

Все не так как кажется на первый взгляд. То, что для программиста выглядит очень похожим, на самом деле в коде может быть совершенно разным. Совершенно.

Давайте посмотрим на внутренний цикл с точки зрения 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 при этом "портится", то его нужно постоянно куда то сохранять. Вот и весь ответ.

→ Ссылка