SlideShare a Scribd company logo
Низкоуровневые оптимизации
Андрей Аксенов, Sphinx
Unigine Open Air 2013
Про что доклад
Про что доклад
• Высокие нагрузки => надо быстро
• Надо быстро => надо оптимизировать
• Надо оптимизировать => и так, и эдак
• Про высокий уровень говорить
затрудняюсь
– С примерами плохо, без примеров плохо
• Поговорю про низкий уровень
Низкий уровень?
• Это, конечно, ассемблер
– И только ассемблер
– Иначе быть не может
– Однако!
Низкий уровень
Низкий уровень
• Однако “ассемблеров” в 2011 году много
– C99, C++03, C++11, Java, C#, LLVM
• Поговорим, выходит, про C/C++
– Несмотря, что
auto f = [](){}; // this is valid fucking C++ now
Почему C/C++
• Все еще ближе всего к железу
• Все еще топовые компиляторы
• Все еще НОД, интегрируется везде
• Все еще много стороннего софта на
• Ну и я других “ассемблеров” “не знаю”
– “Ну то есть что считать за секс”
Про… лозунги
• RT3D == Batch, batch, batch (c) ATI/Nvidia
• VLDB == Shard, shard, shard (c) everyone
• Multicore == Thread, thread, thread (c) Intel
• TDD == Test, test, test (c) K.Beck?
• Agile == Sprint, sprint, sprint (c) M.Fowler?
• Optimizations == ???
Нам лозунг строить…
…а вам с ним жить
BENCH, BENCH, BENCH
– А давайте его отваром ромашки тогда.
Толку не будет, но вреда тоже (c) Гиппократ
256 оттенков серого
• Но нас интересуют только 3
• В целом – диск, память, CPU
• Сегодня – память, CPU
• Завтра – Ulrich Drepper, Agner Fog
– Звоните, все трое и звоните
Что нужно знать про RAM
• Как соотносятся разные цены доступа
• Что память работает не байтами…
• Что бывает L1/L2 кеш
• Что кеш вымывается
• Что (не)выравнивание небесплатно
• Что бывает (и иногда работает) префетч
Что нужно знать про CPU
• Что есть регистры, инструкции, кеш кода
• Что есть целая арифметика, FPU, MMX, SSE
• Что есть mu-ops, pipes, branch prediction,
register renaming, out-of-order execution
• Что все эти чудеса стоят разных “денег”
• Что amd64 “лучше” i686, когда не “хуже”
RAM, про цены в целом
• Bandwidth. Тупое линейное чтение
– 4.6 GB/sec на лаптопе, 12.7 GB/sec на сервере
– Ништяк, ништяк?
• Latency. Такие же 100M int32, stride 4096
– 195 MB/sec, 2.14 sec/100M, ~49 Mreads/sec
– Те. последовательный доступ ~1-2 такта
– Те. случайный доступ ~40-60 тактов
RAM, про цены на практике
• Читать один поток надо мало… но редко
• 100M x { *c++ = (*a++) + (*b++); }
• Линейно 1111 MB/s (вместо 4400+)
• С шагом 4KB выходит 36 MB/s (вместо 195)
– Падение в 5.4 раза
– Жалких 9 Mop/s, 240 тактов на одну (omfg)
RAM, L1 кеш
• Почему так? Потому что L1/L2 cache
• Скачем с шагом N => кеш-миссы =>
тормоза
• Шаг 4..64, ~4400..330 MB/sec, ~2x/шаг
• Шаг 64..1024, ~330..195 MB/sec
• Значит, размер L1 кеш-линии 64 байта :)
RAM, L2 кеш
• А давайте иначе:
вы мне – свою душу,
а я вам – домашний
адрес епископа
Диомида?
RAM, L2 кеш
• Фиксируем шаг 1024, уменьшаем данные
• 100M, …, 4M, 3M, 2.3M == 195 MB/sec
• 2M == 648 MB/sec
• 1M == 1688 MB/sec
• 512K == 1724 MB/sec
• Все сходится, на лаптопе 2MB L2 cache
RAM, про выравнивание
• Лаптоп, 4.6 vs 4.2 G/sec, минус 8.3%
• Сервер, 12.7 vs 11.6 G/sec, минус 7.3%
• Intel, SPARC, ARM
RAM, выводы
RAM, таки выводы
• Если данных много, лучше линейно
• Если линейно никак, лучше мало данных!
• Если можно переложить Тетрис, то нужно
• Если можно, лучше всякое выровнять
– Но держать баланс с локальностью!
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
CPU, про регистры
• i286 – ax, bx, cx, dx, si, di, bp, sp
• i386 – eax, ebx, ecx, edx, esi, edi, ebp, esp
• i686 – eax, ebx, ecx, edx, esi, edi, ebp, esp
• amd64 – rax..rsp, r0..r31
• fpu (aka i287) – fp(0)..fp(7)
• sse – xmm0..xmm7
CPU, про инструкции
• this->a += b
– mov eax, [esi+12] ; The Load
– add eax, ebx ; The Hit
– mov [esi+12], eax ; The Store
• a += b
– add eax, ebx ; The Hit
CPU, VS 2005 vs gcc 4.x
• this->a vs a
• member write-pressure
int iRes = m_iCounter; // skip nonwhitespace
while ( m_iCounter<m_iLimit &&
m_sBuffer[m_iCounter] )
m_iCounter++;
• test1 = 701 msec, test2 = 413 msec
CPU, про кеш кода
• Код это тоже данные и тоже память…
• Внезапно, функции и конвенции вызова
• Внезапно, инлайнить или нет?
– Функции, inline, __forceinline, итп мычки
– Классы, реализация в декларации
– Шаблоны, функторы против коллбэков
CPU, про креативное
• Pipes, начиная с i586 (Pentium-1)
add eax, ebx
add ecx, edx ; pairs
• Дальше ситуация ухудшилась!!!
• Mu-ops, renaming, out-of-order, и всякое
другое увлекательное вуду
CPU, про ветвления
• if ( a!=0 ) { Doit(); } …
cmp eax, 0
jne Label1 ; branch point
call Doit
Label1:
…
• likely(), unlikely() etc
CPU, еще про ветвления
• switch() vs простыня простых if()
• Ни разу не эквивалентны!!!
• switch() оптимизируется, if() нет
• Эффект заметен от 3 (трех) значений
CPU, if-for vs for-if
• for ( int i=0; i<NITER; i++ )
res += ( a==b )
? c*d-e
: (a+b)*c+d;
• For-if, 0.274 sec, res 1 300 000 000
• If-for, 0.184 sec, res 1 300 000 000
CPU, про FPU, SSEx
• Дивный отдельный мир
• fpusum, 44042 usec, msvc default
• fpusum, 15386 usec, msvc /fp:fast
• ssesum, 8453 usec
__m128 res = _mm_set_ps1(0.0f);
while ( p<pmax ) res = _mm_add_ps(res, *p++);
CPU, про FPU, SSEx
• gcc 4.4.3, amd64
• fpusum, 11214 usec
• ssesum, 3414 usec
CPU, SSE unroll
• ssesum, 8453 usec
• ssesum x4, 8240 usec
• ssesum x4 + non-naïve, 8056 usec, +5%
res = _mm_add_ps(res,
_mm_add_ps(_mm_add_ps(p[0], p[1]),
_mm_add_ps(p[2], p[3])));
p += 4;
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Что нужно знать про cc/libc
• Компилятор == gcc, MSVC, (припадочно) ICC
• Про флаги сборки (arch, etc)
• Про конвенции вызова
• Про волшебные define-ы
cc, флаги сборки
• gcc, -march=(i386|i686|core2???)
– Умолчания системы могут удивить
• msvc, /arch:(sse|sse2), /fp:(fast|precise|…)
– По умолчанию голимый FPU
• msvc, /ZI (aka E&C, edit and continue)
– По умолчанию включен… “language”, my ass
• __cdecl, __stdcall, __fastcall
• intrinsics (msvc) aka builtin functions (gcc)
– А это не магия! fabs(), strcpy(), итп
• _SECURE_SCL итп отладочные итераторы
• msvc 2005 по умолчанию…
__cdecl, NO intrinsics, _SECURE_SCL 1
cc, конвенции вызова
cc, аллокации
• Старые недобрые malloc()/free()
– Drop-in замены: nedmalloc, tcmalloc
• Мало “больших” (4-16K+) везде ок
• Много мелких аллокаций везде боль
– Например, 1 M аллокаций по 16 байт
• Ручные пулы все еще работают!
Боевой пример
• Морфологический словарь, libaot
• Сторонний код, между прочим!
• Как именно удалось его взгреть в 3 раза
• Что делает тот критичный код?
– возвращает набор лемм по словоформе
– СТАЛИ -> СТАТЬ, СТАЛЬ
История любви
1. ref, wall 11.9
2. magic1, wall 10.1, 1.178x relative, 1.178x total
3. magic123, wall 9.3, 1.086x relative, 1.279x total
4. currpath, wall 8.0, 1.162x relative, 1.487x total
5. noformsort, wall 6.9, 1.159x relative, 1.724x total
6. fastuc, wall 6.3, 1.095x relative, 1.888x total
7. dwordres, wall 5.9, 1.067x relative, 2.016x total
8. ptrres, wall 4.2, 1.404x relative, 2.833x total
9. manrecurse, wall 4.0, 1.050x relative, 2.975x total
Шаги 1, 2
• +17.8% (magic1)
– Выкидываем 1-буквенные “слова”
– Выкидываем слова “нерусские”
• +8.6% (magic23)
– Выкидываем наиболее частые 2/3-буквенные
– И ведь всего-то 18 слов
Шаг 3
• Было
std::string currentPath;
DoRecursiveStuff ( currentPath, … );
• Стало +16.2% (currpath)
BYTE sPath[128];
DoRecursiveStuff ( sPath, 0, … );
Шаг 4
• Было
vector< pair<string,int> > forms;
Get ( forms );
Sort ( forms );
return forms[0];
Шаг 4
• Стало +15.9% (noformsort)
vector<int> indexes; string currentBest;
Get ( indexes );
for ( int i=0; i<indexes.size(); i++ )
CheckAndUpdate ( currentBest );
return currentBest;
Шаг 5
• Был фарш RmlMakeUpper, FilterSrc
• Приводило регистр, заменяло букву Ё
• Стала прегенерация BYTE m_UC[256];
while ( *p ) { *p = m_UC[*p]; p++; }
• +9.5% (fastuc)
Шаг 6
• Гонялся и возвращался vector<WORD[3]>,
получаемый распаковкой некоего DWORD
• Заменил на vector<DWORD>, в нужных
местах добавил распаковку на месте
• +9.5% (dwordres)
Шаг 7/8
• Результат писало в vector<DWORD> & Infos
• Заменил на DWORD[12], с маркером конца
– Максимальная длина результата 6
• +40.4% (ptrres) !!!
• vector<DWORD> g_res + g_res.reserve() толк
тоже давали, но меньше (очевидно)
Шаг 8/8
• Развернул inner loop из рекурсии в цикл
• Было
int Count = GetChildrenCount(NodeNo); for …
• Стало +5% (manrecurse)
int iChild[MAX_DEPTH], iChildMax[MAX_DEPTH];
while ( iLvl>=0 ) while ( iChild[iLvl]<iMax[iLvl] ) …
Неявный шаг номер 0
• BENCH, BENCH, BENCH
• Как следствие profile, profile, profile
• Черновик, замер, чистовик
• Десяток чистовиков был закоммитан
• Десяток черновиков сразу выкинут
Сводка фокусов
• Душим fastpath, прямо в коде
– Шаги 1, 2
• Душим RAM/stack pressure аргументов
– Шаги 3, 4, 6, 7, 8
• Душим сложность, тупое упрощение
влоб!!!
– Шаг 5
Сводка других фокусов
• Душим лишнюю индирекцию
• Душим аллокации, пулим всякое
• Душим использование RAM (локальность!)
• Душим “плохие” общие структуры
• Душим arch-specific фокусы (switch, sse,
memb-pressure, for-if, ptr-walks, movzx, …)
Сводка третьих фокусов
• И еще несколько десятков всяких
удивительных мелочей
• Из которых лично я…
Мораль про приемы
• Приемов много, общее правило одно
• Идеально вообще не работать!!!
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Мораль про приемы
• Но уж если работать приходится, то по
минимуму
• Как именно схалявить – вопрос не всегда
простой
• Приходится проявляться изворотливость!
Мораль про процесс
• Запрофайлил, забенчмаркал, попробовал
• Намылил, прополоскал, отжал, повторил
• Каждый лишний 1% не лишний
• Курочка по зернышку…
ВОПРОСЫ?!!

More Related Content

PPTX
С одним плюсом (Андрей Аксёнов)
PDF
Как впихнуть утро в сосновом лесу в 4 килобайта. Иван Авдеев. UNIGINE Open Ai...
PPT
Цена абстракции, Андрей Аксёнов (Sphinx)
PDF
Парсим CSS
PDF
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
PPTX
Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
PPTX
Как устроена MySQL-репликация / Андрей Аксенов (Sphinx)
PDF
Отладка производительности приложения на Erlang / Максим Лапшин (Erlyvideo)
С одним плюсом (Андрей Аксёнов)
Как впихнуть утро в сосновом лесу в 4 килобайта. Иван Авдеев. UNIGINE Open Ai...
Цена абстракции, Андрей Аксёнов (Sphinx)
Парсим CSS
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
Как устроена MySQL-репликация / Андрей Аксенов (Sphinx)
Отладка производительности приложения на Erlang / Максим Лапшин (Erlyvideo)

What's hot (19)

PPTX
Как устроен поиск / Андрей Аксенов (Sphinx)
ODP
Top10 доводов против языка Ruby
PPTX
Опыт эксплуатации большого проекта на Ruby
PDF
Практика совместного использования Lua и C в opensource спам-фильтре Rspamd /...
PPTX
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
PPTX
Управление памятью в CPython
PDF
Лекция 6. Стандарт OpenMP
PPTX
Выбираем поисковик умом головы, Андрей Аксенов (Sphinx)
PPT
Как устроен NoSQL, Андрей Аксенов (Sphinx)
PDF
Лекция 6. Стандарт OpenMP
PPTX
Крадущийся сервер, затаившийся диод (Андрей Аксенов)
PDF
Язык Lua — секреты производительности / Ник Заварицкий (Mail.ru)
PDF
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
PDF
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
PDF
Семинар 12. Параллельное программирование на MPI (часть 5)
ODP
Boost.Algorithm: что, зачем и почему
PDF
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
PDF
С чего начать внедрение Hadoop в компании. Доклад Алексея Еремихина (Badoo).
PDF
Android: Как написать приложение, которое не тормозит
Как устроен поиск / Андрей Аксенов (Sphinx)
Top10 доводов против языка Ruby
Опыт эксплуатации большого проекта на Ruby
Практика совместного использования Lua и C в opensource спам-фильтре Rspamd /...
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
Управление памятью в CPython
Лекция 6. Стандарт OpenMP
Выбираем поисковик умом головы, Андрей Аксенов (Sphinx)
Как устроен NoSQL, Андрей Аксенов (Sphinx)
Лекция 6. Стандарт OpenMP
Крадущийся сервер, затаившийся диод (Андрей Аксенов)
Язык Lua — секреты производительности / Ник Заварицкий (Mail.ru)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Семинар 12. Параллельное программирование на MPI (часть 5)
Boost.Algorithm: что, зачем и почему
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
С чего начать внедрение Hadoop в компании. Доклад Алексея Еремихина (Badoo).
Android: Как написать приложение, которое не тормозит
Ad

Viewers also liked (20)

PPTX
Просто, нудно, сложно. Андрей Аксенов. Unigine Open Air 2013
PDF
Rbs sept-okt2016
PPT
Портирование C++ приложений на FLASCC: опыт Unreal Engine 3. Павел Наказненко...
PPSX
La valentina
PPT
PPS
Đến một lúc
PDF
Hand-out event Entertainment & Media, now what?! door Stephan Fellinger
PDF
2016 02-27-27ste bibbertocht
PPSX
Manipulem plastilina
PPTX
The Jurassic Coast
PPTX
Story board for speaking
PPT
Psicomotricitat al pati 1
PDF
Kako redizajnom telefonskih govornica unaprijediti turizam?
PPT
Anglo irish treaty_documents_question
PDF
Are You Ready for Stage 2 Meaningful Use?
PPTX
Common Areas of Carnival Magic
PDF
Rbs juli-augustus 2015
PPSX
Fem col·lage
PPTX
Trường UV ESL - UV ESL Center
PDF
Creating a Culture of Accountability - Business Manager
Просто, нудно, сложно. Андрей Аксенов. Unigine Open Air 2013
Rbs sept-okt2016
Портирование C++ приложений на FLASCC: опыт Unreal Engine 3. Павел Наказненко...
La valentina
Đến một lúc
Hand-out event Entertainment & Media, now what?! door Stephan Fellinger
2016 02-27-27ste bibbertocht
Manipulem plastilina
The Jurassic Coast
Story board for speaking
Psicomotricitat al pati 1
Kako redizajnom telefonskih govornica unaprijediti turizam?
Anglo irish treaty_documents_question
Are You Ready for Stage 2 Meaningful Use?
Common Areas of Carnival Magic
Rbs juli-augustus 2015
Fem col·lage
Trường UV ESL - UV ESL Center
Creating a Culture of Accountability - Business Manager
Ad

Similar to Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013 (20)

PPT
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
PPT
Низкоуровневая Оптимизация (Андрей Аксенов)
PPT
PPTX
Крадущийся сервер, затаившийся диод
PPTX
Эффективное использование x86-совместимых CPU (Алексей Тутубалин)
PPTX
Доклад на Highload-2012
PDF
Поговорим про память
PPTX
Доклад в Mail.ru 01.11.12
PDF
Асинхронная репликация без цензуры, Олег Царёв (Mail.ru Group)
PDF
Java Platform Performance BoF
PDF
HighLoad весна 2014 лекция 5
PDF
Reinventing the wheel - why do it and how to feel good about it - Julik Tarkh...
PPT
CUDA Course 2010 at MSU
PDF
Tempesta FW: challenges, internals, use cases / Александр Крижановский (Tempe...
PPT
Андрей Аксенов "Магия сжатия"
PDF
Сергей Житинский, Александр Чистяков (Git in Sky)
PPTX
Жизнь проекта на production советы по эксплуатации / Николай Сивко (okmeter.io)
PPTX
Жизнь проекта на production
PDF
Open Source SQL-базы данных вступили в эру миллионов запросов в секунду / Фед...
PDF
OpenSource SQL Databases Enter Millions Queries per Second Era
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
Низкоуровневая Оптимизация (Андрей Аксенов)
Крадущийся сервер, затаившийся диод
Эффективное использование x86-совместимых CPU (Алексей Тутубалин)
Доклад на Highload-2012
Поговорим про память
Доклад в Mail.ru 01.11.12
Асинхронная репликация без цензуры, Олег Царёв (Mail.ru Group)
Java Platform Performance BoF
HighLoad весна 2014 лекция 5
Reinventing the wheel - why do it and how to feel good about it - Julik Tarkh...
CUDA Course 2010 at MSU
Tempesta FW: challenges, internals, use cases / Александр Крижановский (Tempe...
Андрей Аксенов "Магия сжатия"
Сергей Житинский, Александр Чистяков (Git in Sky)
Жизнь проекта на production советы по эксплуатации / Николай Сивко (okmeter.io)
Жизнь проекта на production
Open Source SQL-базы данных вступили в эру миллионов запросов в секунду / Фед...
OpenSource SQL Databases Enter Millions Queries per Second Era

More from Unigine Corp. (13)

PDF
Продажи на азиатский рынок (Ден Шергин)
PPTX
Технический писатель: ожидание vs реальность (Ольга Кириченко) - DocFactor 2016
PPTX
Анатомия одного кадра в Unigine Engine (Леонид Лубенко) - DEBUG TiME #3 2016
PPTX
Базовые понятия 3D графики (Ден Шергин) - DEBUG TiME #3 2016
PPTX
Переход к визуализации с учетом физических свойств материалов на примере 3D п...
PPTX
Коротенько про веб-тесты. Екатерина Попова. Debug time#2 2014
PPTX
Про автотесты, фреймворки и железки. Андрей Баюн. Debug time#2 2014
PPTX
20 проектов, 6 платформ, больше миллиона строк кода, 3 qa инженера. Дмитрий К...
PPT
Геймификация - играя делаем игры. Наталья Оглоблина. Unigine Open Air 2013
PPTX
Технологии Alawar для создания игр: какие есть сегодня и какие будут завтра. ...
PDF
Руководство для программистов по устройству на работу в Unigine
PDF
Особенности разработки программных продуктов для международного рынка (Unigin...
PDF
Разработка мультиплатформенных 3D игр (Unigine, CodeFest2012)
Продажи на азиатский рынок (Ден Шергин)
Технический писатель: ожидание vs реальность (Ольга Кириченко) - DocFactor 2016
Анатомия одного кадра в Unigine Engine (Леонид Лубенко) - DEBUG TiME #3 2016
Базовые понятия 3D графики (Ден Шергин) - DEBUG TiME #3 2016
Переход к визуализации с учетом физических свойств материалов на примере 3D п...
Коротенько про веб-тесты. Екатерина Попова. Debug time#2 2014
Про автотесты, фреймворки и железки. Андрей Баюн. Debug time#2 2014
20 проектов, 6 платформ, больше миллиона строк кода, 3 qa инженера. Дмитрий К...
Геймификация - играя делаем игры. Наталья Оглоблина. Unigine Open Air 2013
Технологии Alawar для создания игр: какие есть сегодня и какие будут завтра. ...
Руководство для программистов по устройству на работу в Unigine
Особенности разработки программных продуктов для международного рынка (Unigin...
Разработка мультиплатформенных 3D игр (Unigine, CodeFest2012)

Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013

  • 3. Про что доклад • Высокие нагрузки => надо быстро • Надо быстро => надо оптимизировать • Надо оптимизировать => и так, и эдак • Про высокий уровень говорить затрудняюсь – С примерами плохо, без примеров плохо • Поговорю про низкий уровень
  • 4. Низкий уровень? • Это, конечно, ассемблер – И только ассемблер – Иначе быть не может – Однако!
  • 6. Низкий уровень • Однако “ассемблеров” в 2011 году много – C99, C++03, C++11, Java, C#, LLVM • Поговорим, выходит, про C/C++ – Несмотря, что auto f = [](){}; // this is valid fucking C++ now
  • 7. Почему C/C++ • Все еще ближе всего к железу • Все еще топовые компиляторы • Все еще НОД, интегрируется везде • Все еще много стороннего софта на • Ну и я других “ассемблеров” “не знаю” – “Ну то есть что считать за секс”
  • 8. Про… лозунги • RT3D == Batch, batch, batch (c) ATI/Nvidia • VLDB == Shard, shard, shard (c) everyone • Multicore == Thread, thread, thread (c) Intel • TDD == Test, test, test (c) K.Beck? • Agile == Sprint, sprint, sprint (c) M.Fowler? • Optimizations == ???
  • 10. …а вам с ним жить BENCH, BENCH, BENCH – А давайте его отваром ромашки тогда. Толку не будет, но вреда тоже (c) Гиппократ
  • 11. 256 оттенков серого • Но нас интересуют только 3 • В целом – диск, память, CPU • Сегодня – память, CPU • Завтра – Ulrich Drepper, Agner Fog – Звоните, все трое и звоните
  • 12. Что нужно знать про RAM • Как соотносятся разные цены доступа • Что память работает не байтами… • Что бывает L1/L2 кеш • Что кеш вымывается • Что (не)выравнивание небесплатно • Что бывает (и иногда работает) префетч
  • 13. Что нужно знать про CPU • Что есть регистры, инструкции, кеш кода • Что есть целая арифметика, FPU, MMX, SSE • Что есть mu-ops, pipes, branch prediction, register renaming, out-of-order execution • Что все эти чудеса стоят разных “денег” • Что amd64 “лучше” i686, когда не “хуже”
  • 14. RAM, про цены в целом • Bandwidth. Тупое линейное чтение – 4.6 GB/sec на лаптопе, 12.7 GB/sec на сервере – Ништяк, ништяк? • Latency. Такие же 100M int32, stride 4096 – 195 MB/sec, 2.14 sec/100M, ~49 Mreads/sec – Те. последовательный доступ ~1-2 такта – Те. случайный доступ ~40-60 тактов
  • 15. RAM, про цены на практике • Читать один поток надо мало… но редко • 100M x { *c++ = (*a++) + (*b++); } • Линейно 1111 MB/s (вместо 4400+) • С шагом 4KB выходит 36 MB/s (вместо 195) – Падение в 5.4 раза – Жалких 9 Mop/s, 240 тактов на одну (omfg)
  • 16. RAM, L1 кеш • Почему так? Потому что L1/L2 cache • Скачем с шагом N => кеш-миссы => тормоза • Шаг 4..64, ~4400..330 MB/sec, ~2x/шаг • Шаг 64..1024, ~330..195 MB/sec • Значит, размер L1 кеш-линии 64 байта :)
  • 17. RAM, L2 кеш • А давайте иначе: вы мне – свою душу, а я вам – домашний адрес епископа Диомида?
  • 18. RAM, L2 кеш • Фиксируем шаг 1024, уменьшаем данные • 100M, …, 4M, 3M, 2.3M == 195 MB/sec • 2M == 648 MB/sec • 1M == 1688 MB/sec • 512K == 1724 MB/sec • Все сходится, на лаптопе 2MB L2 cache
  • 19. RAM, про выравнивание • Лаптоп, 4.6 vs 4.2 G/sec, минус 8.3% • Сервер, 12.7 vs 11.6 G/sec, минус 7.3% • Intel, SPARC, ARM
  • 21. RAM, таки выводы • Если данных много, лучше линейно • Если линейно никак, лучше мало данных! • Если можно переложить Тетрис, то нужно • Если можно, лучше всякое выровнять – Но держать баланс с локальностью!
  • 24. CPU, про регистры • i286 – ax, bx, cx, dx, si, di, bp, sp • i386 – eax, ebx, ecx, edx, esi, edi, ebp, esp • i686 – eax, ebx, ecx, edx, esi, edi, ebp, esp • amd64 – rax..rsp, r0..r31 • fpu (aka i287) – fp(0)..fp(7) • sse – xmm0..xmm7
  • 25. CPU, про инструкции • this->a += b – mov eax, [esi+12] ; The Load – add eax, ebx ; The Hit – mov [esi+12], eax ; The Store • a += b – add eax, ebx ; The Hit
  • 26. CPU, VS 2005 vs gcc 4.x • this->a vs a • member write-pressure int iRes = m_iCounter; // skip nonwhitespace while ( m_iCounter<m_iLimit && m_sBuffer[m_iCounter] ) m_iCounter++; • test1 = 701 msec, test2 = 413 msec
  • 27. CPU, про кеш кода • Код это тоже данные и тоже память… • Внезапно, функции и конвенции вызова • Внезапно, инлайнить или нет? – Функции, inline, __forceinline, итп мычки – Классы, реализация в декларации – Шаблоны, функторы против коллбэков
  • 28. CPU, про креативное • Pipes, начиная с i586 (Pentium-1) add eax, ebx add ecx, edx ; pairs • Дальше ситуация ухудшилась!!! • Mu-ops, renaming, out-of-order, и всякое другое увлекательное вуду
  • 29. CPU, про ветвления • if ( a!=0 ) { Doit(); } … cmp eax, 0 jne Label1 ; branch point call Doit Label1: … • likely(), unlikely() etc
  • 30. CPU, еще про ветвления • switch() vs простыня простых if() • Ни разу не эквивалентны!!! • switch() оптимизируется, if() нет • Эффект заметен от 3 (трех) значений
  • 31. CPU, if-for vs for-if • for ( int i=0; i<NITER; i++ ) res += ( a==b ) ? c*d-e : (a+b)*c+d; • For-if, 0.274 sec, res 1 300 000 000 • If-for, 0.184 sec, res 1 300 000 000
  • 32. CPU, про FPU, SSEx • Дивный отдельный мир • fpusum, 44042 usec, msvc default • fpusum, 15386 usec, msvc /fp:fast • ssesum, 8453 usec __m128 res = _mm_set_ps1(0.0f); while ( p<pmax ) res = _mm_add_ps(res, *p++);
  • 33. CPU, про FPU, SSEx • gcc 4.4.3, amd64 • fpusum, 11214 usec • ssesum, 3414 usec
  • 34. CPU, SSE unroll • ssesum, 8453 usec • ssesum x4, 8240 usec • ssesum x4 + non-naïve, 8056 usec, +5% res = _mm_add_ps(res, _mm_add_ps(_mm_add_ps(p[0], p[1]), _mm_add_ps(p[2], p[3]))); p += 4;
  • 36. Что нужно знать про cc/libc • Компилятор == gcc, MSVC, (припадочно) ICC • Про флаги сборки (arch, etc) • Про конвенции вызова • Про волшебные define-ы
  • 37. cc, флаги сборки • gcc, -march=(i386|i686|core2???) – Умолчания системы могут удивить • msvc, /arch:(sse|sse2), /fp:(fast|precise|…) – По умолчанию голимый FPU • msvc, /ZI (aka E&C, edit and continue) – По умолчанию включен… “language”, my ass
  • 38. • __cdecl, __stdcall, __fastcall • intrinsics (msvc) aka builtin functions (gcc) – А это не магия! fabs(), strcpy(), итп • _SECURE_SCL итп отладочные итераторы • msvc 2005 по умолчанию… __cdecl, NO intrinsics, _SECURE_SCL 1 cc, конвенции вызова
  • 39. cc, аллокации • Старые недобрые malloc()/free() – Drop-in замены: nedmalloc, tcmalloc • Мало “больших” (4-16K+) везде ок • Много мелких аллокаций везде боль – Например, 1 M аллокаций по 16 байт • Ручные пулы все еще работают!
  • 40. Боевой пример • Морфологический словарь, libaot • Сторонний код, между прочим! • Как именно удалось его взгреть в 3 раза • Что делает тот критичный код? – возвращает набор лемм по словоформе – СТАЛИ -> СТАТЬ, СТАЛЬ
  • 41. История любви 1. ref, wall 11.9 2. magic1, wall 10.1, 1.178x relative, 1.178x total 3. magic123, wall 9.3, 1.086x relative, 1.279x total 4. currpath, wall 8.0, 1.162x relative, 1.487x total 5. noformsort, wall 6.9, 1.159x relative, 1.724x total 6. fastuc, wall 6.3, 1.095x relative, 1.888x total 7. dwordres, wall 5.9, 1.067x relative, 2.016x total 8. ptrres, wall 4.2, 1.404x relative, 2.833x total 9. manrecurse, wall 4.0, 1.050x relative, 2.975x total
  • 42. Шаги 1, 2 • +17.8% (magic1) – Выкидываем 1-буквенные “слова” – Выкидываем слова “нерусские” • +8.6% (magic23) – Выкидываем наиболее частые 2/3-буквенные – И ведь всего-то 18 слов
  • 43. Шаг 3 • Было std::string currentPath; DoRecursiveStuff ( currentPath, … ); • Стало +16.2% (currpath) BYTE sPath[128]; DoRecursiveStuff ( sPath, 0, … );
  • 44. Шаг 4 • Было vector< pair<string,int> > forms; Get ( forms ); Sort ( forms ); return forms[0];
  • 45. Шаг 4 • Стало +15.9% (noformsort) vector<int> indexes; string currentBest; Get ( indexes ); for ( int i=0; i<indexes.size(); i++ ) CheckAndUpdate ( currentBest ); return currentBest;
  • 46. Шаг 5 • Был фарш RmlMakeUpper, FilterSrc • Приводило регистр, заменяло букву Ё • Стала прегенерация BYTE m_UC[256]; while ( *p ) { *p = m_UC[*p]; p++; } • +9.5% (fastuc)
  • 47. Шаг 6 • Гонялся и возвращался vector<WORD[3]>, получаемый распаковкой некоего DWORD • Заменил на vector<DWORD>, в нужных местах добавил распаковку на месте • +9.5% (dwordres)
  • 48. Шаг 7/8 • Результат писало в vector<DWORD> & Infos • Заменил на DWORD[12], с маркером конца – Максимальная длина результата 6 • +40.4% (ptrres) !!! • vector<DWORD> g_res + g_res.reserve() толк тоже давали, но меньше (очевидно)
  • 49. Шаг 8/8 • Развернул inner loop из рекурсии в цикл • Было int Count = GetChildrenCount(NodeNo); for … • Стало +5% (manrecurse) int iChild[MAX_DEPTH], iChildMax[MAX_DEPTH]; while ( iLvl>=0 ) while ( iChild[iLvl]<iMax[iLvl] ) …
  • 50. Неявный шаг номер 0 • BENCH, BENCH, BENCH • Как следствие profile, profile, profile • Черновик, замер, чистовик • Десяток чистовиков был закоммитан • Десяток черновиков сразу выкинут
  • 51. Сводка фокусов • Душим fastpath, прямо в коде – Шаги 1, 2 • Душим RAM/stack pressure аргументов – Шаги 3, 4, 6, 7, 8 • Душим сложность, тупое упрощение влоб!!! – Шаг 5
  • 52. Сводка других фокусов • Душим лишнюю индирекцию • Душим аллокации, пулим всякое • Душим использование RAM (локальность!) • Душим “плохие” общие структуры • Душим arch-specific фокусы (switch, sse, memb-pressure, for-if, ptr-walks, movzx, …)
  • 53. Сводка третьих фокусов • И еще несколько десятков всяких удивительных мелочей • Из которых лично я…
  • 54. Мораль про приемы • Приемов много, общее правило одно • Идеально вообще не работать!!!
  • 56. Мораль про приемы • Но уж если работать приходится, то по минимуму • Как именно схалявить – вопрос не всегда простой • Приходится проявляться изворотливость!
  • 57. Мораль про процесс • Запрофайлил, забенчмаркал, попробовал • Намылил, прополоскал, отжал, повторил • Каждый лишний 1% не лишний • Курочка по зернышку…