Сборник кусков кода

Различные примеры кода.

О производительности структур данных

Abyx Abyx
#include <cstdlib>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <numeric>
#include <string>
#include <thread>
#include <vector>

struct Obj
{
    Obj* next;
    unsigned x;

    Obj() : x(::rand()) {}
    void update()
    {
        ++x; // как-то меняем данные объекта
    }
};

template<typename F>
void test(const char* name, F f)
{
    std::vector<unsigned> times(100);

    for (auto& dt : times)
    {
        std::this_thread::yield();

        auto t1 = std::chrono::high_resolution_clock::now();
        f();
        auto t2 = std::chrono::high_resolution_clock::now();

        dt = (unsigned)std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
    }

    std::cout << name << ": " << std::accumulate(times.begin(), times.end(), 0) / times.size() << " ms" << std::endl;
}

int main()
{
    std::srand((unsigned)std::time(nullptr));

    std::vector<Obj> vObj(1 * 1000 * 1000);

    std::vector<Obj*> vPtr;
    for (auto& o : vObj)
        vPtr.push_back(&o);

    std::random_shuffle(vPtr.begin(), vPtr.end()); // убиваем memory locality при последовательном доступе

    Obj* lst = nullptr;
    for (auto p : vPtr)
    {
        p->next = lst;
        lst = p;
    }

    test("vector<Obj>", [&]{ for (auto& o : vObj) o.update(); });
    test("vector<Obj*>", [&]{ for (auto& o : vPtr) o->update(); });
    test("intrusive list", [&]{ for (auto o = lst; o; o = o->next) o->update(); });
}


Типичный результат на моей машине
vector<Obj>: 0 ms
vector<Obj*>: 10 ms
intrusive list: 76 ms


Почему так?
В случае с vector<Obj> объект лежат друг за другом, и эффективно читаются в кеш процессора
У vector<Obj*> перемешаны указатели, по этому кешировать доступ к данным объектов не получается.
Однако, сам vector<Obj*> прекрасно кешируется, а вот для связанного списка кеширование не работает вообще.
wildwind
wildwind
21.10.2012 06:42
Говорить о "производительности структур данных" — некорректно. Производительность может быть у алгоритмов, работающих на этих структурах. А еще точнее — у конкретных реализаций этих алгоритмов.
Философ
Философ
21.10.2012 07:55
Здравствуйте, wildwind, Вы писали:

W>Говорить о "производительности структур данных" — некорректно. Производительность может быть у алгоритмов, работающих на этих структурах. А еще точнее — у конкретных реализаций этих алгоритмов.


точно?

в некоторых случаях, скорость работы с вот такими массивами
int A[MaxX][MaxY]
и
int B[MaxY][MaxX]

при использовании одних и тех же алгоритмов, может различаться на несколько порядков, т.е. настолько сильно, что иногда приходится это учитывать и оптимизировать.
dilmah
dilmah
21.10.2012 08:10
A>
A>vector<Obj*>: 10 ms
A>intrusive list: 76 ms
A>


может это быть свяхано с тем что у последней структуры степень параллелизма меньще??
понятно что если это вектор указаетлей то операции update над разными элементами независимы и даже на 1 роцуссоре конвейер моэет быть испольхован более эффективно

А у последней структуры каждый следующий элемммент хависит от предыдущего
Философ
Философ
21.10.2012 08:32
Здравствуйте, dilmah, Вы писали:

A>>
A>>vector<Obj*>: 10 ms
A>>intrusive list: 76 ms
A>>


D>может это быть свяхано с тем что у последней структуры степень параллелизма меньще??


степень чего?
(похоже, я не в курсе)
Философ
Философ
21.10.2012 08:31
Remark на эту тему хорошо писал.

http://www.rsdn.ru/forum/philosophy/2929998.1
dilmah
dilmah
21.10.2012 08:39
Ф>http://www.rsdn.ru/forum/philosophy/2929998.1

ну вообще-то в последних двух случаях одинаково рандомный доступ к памяти. Вопрос в том почему 2 последних отличаются. То что первый бысттрее это всем понятно.
Философ
Философ
21.10.2012 09:01
Здравствуйте, dilmah, Вы писали:


Ф>>http://www.rsdn.ru/forum/philosophy/2929998.1


D>ну вообще-то в последних двух случаях одинаково рандомный доступ к памяти. Вопрос в том почему 2 последних отличаются.


точно?

http://files.rsdn.org/67254/MemAccess2.PNG
dilmah
dilmah
21.10.2012 09:17
D>>ну вообще-то в последних двух случаях одинаково рандомный доступ к памяти. Вопрос в том почему 2 последних отличаются.

Ф>точно?


указатели лежат рядом с данными
Я уверен, что количество cache misses несильно отличается в 2 последних случаях

Разница именно в степени независимости данных, что дает разную загрузку конвейера
Ikemefula
Ikemefula
31.10.2012 03:11
Здравствуйте, Abyx, Вы писали:

A>Почему так?

A>В случае с vector<Obj> объект лежат друг за другом, и эффективно читаются в кеш процессора
A>У vector<Obj*> перемешаны указатели, по этому кешировать доступ к данным объектов не получается.
A>Однако, сам vector<Obj*> прекрасно кешируется, а вот для связанного списка кеширование не работает вообще.

Разницу дает вот что
for (auto& o : vPtr) ...

for (auto o = lst; o; o = o->next) ...


Слишком много лишних присваиваний. То есть, нужно взять да глянуть в ассемблерный код, что бы убедиться, что третий вариант цикла дает самый неэффективный код, а массивы очень сильно оптимизированы.
Abyx
Abyx
31.10.2012 03:36
Здравствуйте, Ikemefula, Вы писали:

A>>Почему так?

A>>В случае с vector<Obj> объект лежат друг за другом, и эффективно читаются в кеш процессора
A>>У vector<Obj*> перемешаны указатели, по этому кешировать доступ к данным объектов не получается.
A>>Однако, сам vector<Obj*> прекрасно кешируется, а вот для связанного списка кеширование не работает вообще.

I>Разницу дает вот что

I>
I>for (auto& o : vPtr) ...

I>for (auto o = lst; o; o = o->next) ...
I>


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


*много* это одно?
ассемблерный код несложно сгенерить в уме:
for (auto& o : vPtr) ++o->x; это

    mov esi, [vPtr._MyFirst]
    mov edi, [vPtr._MyLast]
loo:
    cmp esi, edi
    jz end_loop ; низкая вероятность перехода
    mov eax, [esi]
    add [eax+4], 1
    add esi, 4
    jmp loo
end_loop:


for (auto o = lst; o; o = o->next) ++o->x; это

    mov esi, [lst]
loo:
    test esi, esi
    jz end_loop ; низкая вероятность перехода
    mov eax, esi
    mov esi, [esi]
    add [eax+4], 1
    jmp loo
end_loop:
Ikemefula
Ikemefula
31.10.2012 04:00
Здравствуйте, Abyx, Вы писали:

A>*много* это одно?

A>ассемблерный код несложно сгенерить в уме:

Сложно, и ты, к тому же, привел не тот код.

0020142D  mov         eax,ebx  
0020142F  cmp         ebx,edi  
00201431  je          main+1CFh (020143Fh)  
00201433  mov         ecx,dword ptr [eax]  
00201435  add         eax,4  
00201438  inc         dword ptr [ecx+4]  // вызов update
0020143B  cmp         eax,edi  
0020143D  jne         main+1C3h (0201433h)  


0020143F  test        edx,edx  
00201441  je          main+1DCh (020144Ch)  
00201443  inc         dword ptr [edx+4]  // вызов update
00201446  mov         edx,dword ptr [edx]  // причина тормозов
00201448  test        edx,edx  
0020144A  jne         main+1D3h (0201443h)
Abyx
Abyx
31.10.2012 04:36
Здравствуйте, Ikemefula, Вы писали:

A>>ассемблерный код несложно сгенерить в уме:


I>Сложно, и ты, к тому же, привел не тот код.

почему не тот? где там разница?

I>00201446 mov edx,dword ptr [edx] // причина тормозов

и в чем тут причина? лишнее присваивание?
Ikemefula
Ikemefula
31.10.2012 09:20
Здравствуйте, Abyx, Вы писали:

I>>Сложно, и ты, к тому же, привел не тот код.

A>почему не тот? где там разница?

А что, лень поставить два фрагмента в один редактор и сравнить ?

I>>00201446 mov edx,dword ptr [edx] // причина тормозов

A>и в чем тут причина? лишнее присваивание?

AGI
Abyx
Abyx
31.10.2012 09:34
Здравствуйте, Ikemefula, Вы писали:

I>Здравствуйте, Abyx, Вы писали:


I>>>Сложно, и ты, к тому же, привел не тот код.

A>>почему не тот? где там разница?

I>А что, лень поставить два фрагмента в один редактор и сравнить ?


я сравнил и не заметил разницы, за исключением разных регистров и т.п.

I>>>00201446 mov edx,dword ptr [edx] // причина тормозов

A>>и в чем тут причина? лишнее присваивание?

I>AGI


что? http://www.urbandictionary.com/define.php?term=AGI
Ikemefula
Ikemefula
01.11.2012 08:14
Здравствуйте, Abyx, Вы писали:

I>>А что, лень поставить два фрагмента в один редактор и сравнить ?


A>я сравнил и не заметил разницы, за исключением разных регистров и т.п.


Плохо сравнивал.

I>>>>00201446 mov edx,dword ptr [edx] // причина тормозов

A>>>и в чем тут причина? лишнее присваивание?

I>>AGI


A>что? http://www.urbandictionary.com/define.php?term=AGI


И давно ты специальные термины на urbandictionary смотришь ?

Address Generation Interlock