О производительности структур данных
От: Abyx Россия  
Дата: 19.10.12 20:25
Оценка: -1 :)
#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*> прекрасно кешируется, а вот для связанного списка кеширование не работает вообще.
In Zen We Trust
Re: О производительности структур данных
От: wildwind Россия  
Дата: 21.10.12 18:42
Оценка:
Говорить о "производительности структур данных" — некорректно. Производительность может быть у алгоритмов, работающих на этих структурах. А еще точнее — у конкретных реализаций этих алгоритмов.
Re[2]: О производительности структур данных
От: Философ Ад http://vk.com/id10256428
Дата: 21.10.12 19:55
Оценка:
Здравствуйте, wildwind, Вы писали:

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


точно?

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

при использовании одних и тех же алгоритмов, может различаться на несколько порядков, т.е. настолько сильно, что иногда приходится это учитывать и оптимизировать.
Всё сказанное выше — личное мнение, если не указано обратное.
Re: О производительности структур данных
От: dilmah США  
Дата: 21.10.12 20:10
Оценка:
A>
A>vector<Obj*>: 10 ms
A>intrusive list: 76 ms
A>


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

А у последней структуры каждый следующий элемммент хависит от предыдущего
Re: О производительности структур данных
От: Философ Ад http://vk.com/id10256428
Дата: 21.10.12 20:31
Оценка: 15 (1)
Remark на эту тему хорошо писал.

http://www.rsdn.ru/forum/philosophy/2929998.1
Автор: remark
Дата: 25.04.08
Всё сказанное выше — личное мнение, если не указано обратное.
Re[2]: О производительности структур данных
От: Философ Ад http://vk.com/id10256428
Дата: 21.10.12 20:32
Оценка:
Здравствуйте, dilmah, Вы писали:

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


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


степень чего?
(похоже, я не в курсе)
Всё сказанное выше — личное мнение, если не указано обратное.
Re[2]: О производительности структур данных
От: dilmah США  
Дата: 21.10.12 20:39
Оценка:
Ф>http://www.rsdn.ru/forum/philosophy/2929998.1
Автор: remark
Дата: 25.04.08


ну вообще-то в последних двух случаях одинаково рандомный доступ к памяти. Вопрос в том почему 2 последних отличаются. То что первый бысттрее это всем понятно.
Re[3]: О производительности структур данных
От: Философ Ад http://vk.com/id10256428
Дата: 21.10.12 21:01
Оценка:
Здравствуйте, dilmah, Вы писали:


Ф>>http://www.rsdn.ru/forum/philosophy/2929998.1
Автор: remark
Дата: 25.04.08


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


точно?

Всё сказанное выше — личное мнение, если не указано обратное.
Re[4]: О производительности структур данных
От: dilmah США  
Дата: 21.10.12 21:17
Оценка: 5 (1)
D>>ну вообще-то в последних двух случаях одинаково рандомный доступ к памяти. Вопрос в том почему 2 последних отличаются.

Ф>точно?


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

Разница именно в степени независимости данных, что дает разную загрузку конвейера
Re: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.12 15:11
Оценка: :)
Здравствуйте, Abyx, Вы писали:

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

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

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

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


Слишком много лишних присваиваний. То есть, нужно взять да глянуть в ассемблерный код, что бы убедиться, что третий вариант цикла дает самый неэффективный код, а массивы очень сильно оптимизированы.
Re[2]: О производительности структур данных
От: Abyx Россия  
Дата: 31.10.12 15: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:
In Zen We Trust
Re[3]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.12 16: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)
Re[4]: О производительности структур данных
От: Abyx Россия  
Дата: 31.10.12 16:36
Оценка:
Здравствуйте, Ikemefula, Вы писали:

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


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

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

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

и в чем тут причина? лишнее присваивание?
In Zen We Trust
Re[5]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.12 21:20
Оценка: -1
Здравствуйте, Abyx, Вы писали:

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

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

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

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

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

AGI
Re[6]: О производительности структур данных
От: Abyx Россия  
Дата: 31.10.12 21: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
In Zen We Trust
Re[7]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 01.11.12 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
Re[8]: О производительности структур данных
От: Abyx Россия  
Дата: 01.11.12 10:20
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Address Generation Interlock


т.е. ты хочешь сказать, что из за того что две инструкции не выполняются параллельно, мы получили падение производительности в 6-8 раз?
т.е. вместо X + max(T1, T2) получилось X + T1 + T2, и X + max(T1, T2) = (X + T1 + T2) / 6 ?

не, это конечно круто, что ты помнишь что такое Address Generation Interlock, я вот совсем забыл как там работает pipeline, но к счастью я еще не забыл арифметику.
видишь ли, если между инструкциями воткнуть еще несколько инструкций, время работы практически не поменяется

    test("intrusive list (asm)", [&]{
        auto lst_ = lst;
        __asm {
            mov edx, lst_
        loo:
            test edx, edx
            _emit 0x2E
            jz end_loop

            and eax, 3
            add edx, eax
            shr edx, 2
            add edx, edx
            add edx, edx

            inc dword ptr [edx+4]

            and eax, 3
            add edx, eax
            shr edx, 2
            add edx, edx
            add edx, edx

            mov edx, dword ptr [edx]

            and eax, 3
            add edx, eax
            shr edx, 2
            add edx, edx
            add edx, edx

            jmp loo
        end_loop:
        }
    });


Результаты:

vector<Obj> (avg. time: 1 ms)
  0 |######################################## (40)
  1 |###################################################### (54)
  2 |##### (5)
  3 |
  4 |#


vector<Obj*> (avg. time: 10 ms)
  9 |###
 10 |################################################################# (65)
 11 |####################### (23)
 12 |##### (5)
 13 |#
 14 |#
 15 |##


intrusive list (avg. time: 79 ms)
 76 |####### (7)
 77 |######################## (24)
 78 |####################### (23)
 79 |########## (10)
 80 |########## (10)
 81 |##
 82 |####
 83 |###### (6)
 84 |######### (9)
 85 |##
 86 |##
 87 |
 88 |#


intrusive list (asm) (avg. time: 82 ms)
 79 |########## (10)
 80 |####################### (23)
 81 |################## (18)
 82 |################ (16)
 83 |############# (13)
 84 |##### (5)
 85 |####
 86 |##
 87 |##### (5)
 88 |##
 89 |
 90 |
 91 |
 92 |#
 93 |
 94 |#

(по вертикали — время, по горизонтали — количество замеров с этим временем (всего 100 тестов))
In Zen We Trust
Re[9]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 01.11.12 13:31
Оценка: -1
Здравствуйте, Abyx, Вы писали:

A>т.е. ты хочешь сказать, что из за того что две инструкции не выполняются параллельно, мы получили падение производительности в 6-8 раз?

A>т.е. вместо X + max(T1, T2) получилось X + T1 + T2, и X + max(T1, T2) = (X + T1 + T2) / 6 ?

Опережающей выборки не будет — это и есть причина тормозов.


A>(по вертикали — время, по горизонтали — количество замеров с этим временем (всего 100 тестов))


Зеваю.
Re[10]: О производительности структур данных
От: Abyx Россия  
Дата: 01.11.12 13:41
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Опережающей выборки не будет — это и есть причина тормозов.


о... это уже что-то новое, близкое к истине.
а как же "много присваиваний"? а что с Address Generation Interlock?
In Zen We Trust
Re[11]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 01.11.12 13:48
Оценка:
Здравствуйте, Abyx, Вы писали:

I>>Опережающей выборки не будет — это и есть причина тормозов.


A>о... это уже что-то новое, близкое к истине.

A>а как же "много присваиваний"? а что с Address Generation Interlock?

Опережающей выборки не будет как раз из-за AGI.
Re[12]: О производительности структур данных
От: Abyx Россия  
Дата: 01.11.12 14:16
Оценка:
Здравствуйте, Ikemefula, Вы писали:

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


I>>>Опережающей выборки не будет — это и есть причина тормозов.


A>>о... это уже что-то новое, близкое к истине.

A>>а как же "много присваиваний"? а что с Address Generation Interlock?

I>Опережающей выборки не будет как раз из-за AGI.


угу, осталась только самая малость — как-то подтвердить это.
не, я не говорю что там нет AGI, но только как-то неочевидно, что именно AGI дает разницу в производительности
извини, но после того как тебе понадобился пяток постов чтобы сформулировать словосочетание "опережающая выборка", поверить тебе на слово как-то не получается
In Zen We Trust
Re[13]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 01.11.12 14:47
Оценка:
Здравствуйте, Abyx, Вы писали:

A>угу, осталась только самая малость — как-то подтвердить это.

A>не, я не говорю что там нет AGI, но только как-то неочевидно, что именно AGI дает разницу в производительности
A>извини, но после того как тебе понадобился пяток постов чтобы сформулировать словосочетание "опережающая выборка", поверить тебе на слово как-то не получается


Я на асме не писал около 12 лет , кода показать не смогу
Re[14]: О производительности структур данных
От: Abyx Россия  
Дата: 01.11.12 15:26
Оценка: 4 (1)
Здравствуйте, Ikemefula, Вы писали:

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


A>>угу, осталась только самая малость — как-то подтвердить это.

A>>не, я не говорю что там нет AGI, но только как-то неочевидно, что именно AGI дает разницу в производительности
A>>извини, но после того как тебе понадобился пяток постов чтобы сформулировать словосочетание "опережающая выборка", поверить тебе на слово как-то не получается

I> Я на асме не писал около 12 лет , кода показать не смогу


да при чем тут вообще асм? и без асма понятно, что
o = o->next;
o->update();
o = o->next;

можно выполнить только последовательно.

для массива указателей достаточно внести такую же зависимость между итерациями, вместо

        for (auto it = vPtr.begin(); it < vPtr.end(); ++it)
            (*it)->update();


написать

        for (auto it = vPtr.begin(); it < vPtr.end(); it += (*it)->x & 1)
            (*it)->update();


получится

vector<Obj*> (avg. time: 12 ms)
 11 |######################### (25)
 12 |################################################################### (67)
 13 |####### (7)
 14 |#


vector<Obj*> (slow) (avg. time: 92 ms)
 87 |#
 88 |
 89 |########## (10)
 90 |#################### (20)
 91 |############################ (28)
 92 |############## (14)
 93 |############## (14)
 94 |##### (5)
 95 |###### (6)
 96 |
 97 |#
 98 |#


т.е. примерно в 8 раз медленнее, хотя структура данных не поменялась, и мы обрабатываем всего 75% данных
In Zen We Trust
Re[15]: О производительности структур данных
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 01.11.12 15:36
Оценка:
Здравствуйте, Abyx, Вы писали:

I>> Я на асме не писал около 12 лет , кода показать не смогу


A>да при чем тут вообще асм? и без асма понятно, что

A>
A>o = o->next;
o->>update();
A>o = o->next;
A>

A>можно выполнить только последовательно.

То есть, тебе все понятно, но тебе нужны какие то подтверждения ? Я чет не понимаю, чего же ты хочешь
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.