SlideShare uma empresa Scribd logo
1 de 38
Baixar para ler offline
Кирилл Березин
Ведущий программист
Как не сделать
врагами архитектуру
и оптимизацию
14 Ноября 2019
Постановка задачи
● Задача – сменить библиотеку json;
● Json использовался в качестве внутреннего
формата данных;
● Использование шаблонов выражений для
уменьшения кода.
Зачем все это было?
● Исследование показало, что операции с
json занимали не меньше 20 % времени;
● Rapidjson read rate 140 МБ/с
(худший);
● Cpprest sdk 13 МБ/с.
Архитектура программы
l Функциональность
в библиотеках;
l Внутренний формат
данных json-lib.
dll
Json
wrapper
dll
exe
Первые результаты
l Результат перевода теста одной из
библиотек показал:
- Rapidjson 15-25 МБ/с
- Cpp rest sdk 13 МБ/с
Исследуем код cpprest
typedef std::vector<std::pair<utility::string_t, json::value>> storage_type;
Исследуем код cpprest
typedef std::vector<std::pair<utility::string_t, json::value>> storage_type;
Исследуем код cpprest
l Используется move семантика;
l Элементы могут быть сортированы –
бинарный поиск;
l Элементы полиморфные;
l Поставляется в виде отдельной
библиотеки.
Исследуем код Rapidjson
union Data {
String s;
ShortString ss;
...
};
Исследуем код Rapidjson
l Кастомные аллокаторы;
l Микрооптимизации Neon/sse;
l header only – уровень оптимизации
определяется основным кодом.
Сравнительный тест
производительности (-O3).
l Nativejson benchmark (для некоторого i5 3
поколения)
Benchmarking Performance of C++ REST SDK (C++11)
Parse canada.json ... 162.880 ms 13.180 MB/s
Parse citm_catalog.json ... 23.060 ms 71.431 MB/s
Parse twitter.json ... 10.985 ms 54.826 MB/s
Benchmarking Performance of RapidJSON_FullPrec (C++)
Parse canada.json ... 15.184 ms 141.384 MB/s
Parse citm_catalog.json ... 3.109 ms 529.813 MB/s
Parse twitter.json ... 2.084 ms 288.992 MB/s
Сравнительный тест
производительности (-O0).
l Nativejson benchmark (для некоторого i5 3
поколения)
Benchmarking Performance of C++ REST SDK (C++11)
Parse canada.json ... 166.016 ms 12.931 MB/s
Parse citm_catalog.json ... 23.549 ms 69.947 MB/s
Parse twitter.json ... 11.044 ms 54.533 MB/s
Benchmarking Performance of RapidJSON_FullPrec (C++)
Parse canada.json ... 139.193 ms 15.423 MB/s
Parse citm_catalog.json ... 25.075 ms 65.691 MB/s
Parse twitter.json ... 10.639 ms 56.609 MB/s
Шаблоны выражений
l Быстрая, но громозкая реализация;
l Оборачиваем шаблоном, с более
короткой реализацией;
l Требуется оптимизация кода;
l Код по скорости близок к
идеальному.
template<typename T>
struct SArray {
explicit SArray(std::size_t s) : storage(new T[s]), storage_size(s) { ... }
SArray(SArray<T> const& orig) { … }
~SArray() { delete [] storage; }
std::size_t size() const { return storage_size; }
T const& operator[] (std::size_t idx) const {
return storage[idx];
}
T& operator[] (std::size_t idx) {
return storage[idx];
}
T* storage;
std::size_t storage_size;
};
Низкоуровневая
реализация
int main()
{
SArray<double> x(1000), y(1000);
for (auto i = 0 ; i < x.size(); ++i)
x[i] = 1.2 * x[i] + x[i] * y[i];
}
main:
push r12
mov edi, 8000
push rbp
sub rsp, 8
call operator new[](unsigned
long)@PLT
lea rdi, 8[rax]
...
call operator new[](unsigned
long)@PLT
lea rdi, 8[rax]
...
rep stosq
Готовый код конструкторы
.L2:
movupd xmm3, XMMWORD PTR 0[rbp+rax]
movupd xmm0, XMMWORD PTR [r8+rax]
mulpd xmm0, xmm3
movapd xmm1, xmm3
mulpd xmm1, xmm2
addpd xmm0, xmm1
movups XMMWORD PTR 0[rbp+rax], xmm0
add rax, 16
cmp rax, 8000
jne .L2
mov rdi, r8
call operator delete[](void*)@PLT
mov rdi, rbp
call operator delete[](void*)@PLT
x[i] = 1.2 * x[i] + x[i] * y[i];
Готовый код выражение.
template<typename T, typename OP1, typename OP2>
struct A_Mult {
typename A_Traits<OP1>::ExprRef op1; // first operand
typename A_Traits<OP2>::ExprRef op2; // second operand
A_Mult (OP1 const& a, OP2 const& b)
: op1(a), op2(b) { }
T operator[] (std::size_t idx) const {
return op1[idx] * op2[idx];
}
std::size_t size() const {
return std::max(a.size(), b.size());
}
};
Шаблон выражения
Скалярный тип.
template<typename T>
struct A_Scalar {
T const& scalar;
constexpr A_Scalar (T const& v) : scalar(v) { }
constexpr T const& operator[] (std::size_t) const {
return scalar;
}
constexpr std::size_t size() const { return 0; };
};
Обертка.
template<typename T, typename Rep = SArray<T>>
struct Array {
Rep expr_rep; // данные или класс-выражение
explicit Array (std::size_t s) : expr_rep(s) { }
Array (Rep const& rb) : expr_rep(rb) { }
Array& operator= (Array const& b) {
for (std::size_t idx = 0; idx<b.size(); ++idx) {
expr_rep[idx] = b[idx];
}
return *this;
}
std::size_t size() const { return expr_rep.size(); }
decltype(auto) operator[] (std::size_t idx) const {
return expr_rep[idx];
}
T& operator[] (std::size_t idx) {
return expr_rep[idx];
}
};
Обертка.
Оператор
template<typename T, typename R1, typename R2>
Array<T, A_Mult<T,R1,R2>>
operator* (Array<T,R1> const& a, Array<T,R2> const& b) {
return Array<T,A_Mult<T,R1,R2>>
(A_Mult<T,R1,R2>(a.rep(), b.rep()));
}
Как используется
int main()
{
Array<double> x(1000), y(1000);
x = 1.2 * x + x * y;
}
Выражение
Array<double, SArray<double> >&
Array<double, SArray<double> >::operator=<double,
A_Add<double, A_Mult<double, A_Scalar<double>,
SArray<double> >,
A_Mult<double, SArray<double>, SArray<double> > > >
(
Array<double,
A_Add<double, A_Mult<double, A_Scalar<double>,
SArray<double> >,
A_Mult<double, SArray<double>, SArray<double> > > >
const&
)
x = 1.2 * x + x * y;
main:
...
call operator new[](unsigned long)@PLT
...
call operator new[](unsigned long)@PLT
...
rep stosq
...
movaps XMMWORD PTR 80[rsp], xmm0
call Array<double, ...
mov rdi, QWORD PTR 32[rsp]
test rdi, rdi
je .L46
call operator delete[](void*)@PLT
.L46:
mov rdi, QWORD PTR 16[rsp]
test rdi, rdi
je .L47
call operator delete[](void*)@PLT
Array<double, SArray<double> >& Array<double, SArray<double>
>::operator=<double, A_Add<double, A_Mult<double, A_Scalar<double>,
SArray<double> >, A_Mult<double, SArray<double>, SArray<double> > >
>(Array<double, A_Add<double, A_Mult<double, A_Scalar<double>,
SArray<double> >, A_Mult<double, SArray<double>, SArray<double> > >
> const&):
push r15
...
.L26:
mov r9, QWORD PTR [rdi]
mov rsi, QWORD PTR [r12]
add rcx, 1
movsd xmm0, QWORD PTR [r9+rdx]
mov r9, QWORD PTR 0[rbp]
mulsd xmm0, QWORD PTR [rsi+rdx]
mov rsi, QWORD PTR [rbx]
movsd xmm1, QWORD PTR [r9+rdx]
mulsd xmm1, QWORD PTR [rsi]
addsd xmm0, xmm1
movsd QWORD PTR [r11], xmm0
benchmarks
static void Ideal(benchmark::State& state)
{
SArray<double> x(1000), y(1000);
for(auto _ : state)
{
for (auto i = 0 ; i < x.size(); ++i)
x[i] = 1.2 * x[i] + x[i] * y[i];
}
}
BENCHMARK(Ideal);
static void SuperFast(benchmark::State& state)
{
Array<double> x(1000), y(1000);
for(auto _ : state) {
x = 1.2 * x + x * y;
}
}
BENCHMARK(SuperFast);
Benchmark
gcc 9.2.0 (-O3)
Run on (8 X 3400 MHz CPU s)
CPU Caches:
L1 Data 32K (x4)
L1 Instruction 32K (x4)
L2 Unified 256K (x4)
L3 Unified 6144K (x1)
Load Average: 0.33, 0.35, 0.21
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
Ideal 255 ns 255 ns 2693296
SuperFast 346 ns 346 ns 2007497
Benchmark
gcc 9.2.0 (-O0)
Run on (8 X 3400 MHz CPU s)
CPU Caches:
L1 Data 32K (x4)
L1 Instruction 32K (x4)
L2 Unified 256K (x4)
L3 Unified 6144K (x1)
Load Average: 0.33, 0.35, 0.21
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
Ideal 10123 ns 10119 ns 63845
SuperFast 40989 ns 40971 ns 13674
Итог
l Rapidjson быстр, но требует оптимизацию
l Шаблоны выражений требуют
оптимизацию.
l Тестовая библиотека дает
- Rapidjson 15-25 МБ/с
- Cpp rest sdk 13 МБ/с
Исследуем проект
l Часть библиотек с /Z0 (== -O0)
l Часть с включенной оптимизацией
Архитектура кода и оптимизация :
модуль json.o
void readJ(std::string & json)
{
rapidjson::Document doc;
doc.Parse(json.c_str());
}
void readJExternal(std::string const& json, rapidjson::Document& doc)
{
doc.Parse(json.c_str());
}
Архитектура кода и оптимизация:
модуль main.o
static void JSON_ParseInternal(benchmark::State& state)
{
std::ifstream in_("canada.json");
auto json = std::string(std::istreambuf_iterator<char>(in_), std::istreambuf_iterator<char>());
for(auto _ : state) {
readJ(json);
}
}
BENCHMARK(JSON_ParseInternal);
Архитектура кода и оптимизация:
модуль main.o
static void JSON_ParseExternal(benchmark::State& state)
{
std::ifstream in_("canada.json");
auto json = std::string(std::istreambuf_iterator<char>(in_),
std::istreambuf_iterator<char>());
rapidjson::Document doc;
for(auto _ : state) {
readJExternal(json, doc);
}
}
BENCHMARK(JSON_ParseExternal);
Разделение кода и оптимизация:
модуль main.o
static void JSON_CalcSum(benchmark::State& state)
{
std::ifstream in_("canada.json");
auto json = std::string(std::istreambuf_iterator<char>(in_),
std::istreambuf_iterator<char>());
rapidjson::Document doc;
readJExternal(json, doc);
for(auto _ : state) {
const auto& a = doc["features"][0]["geometry"]["coordinates"][0];
double result = 0.0;
for(rapidjson::SizeType i = 0; i < a.Size(); ++i)
result += a[i][0].GetDouble() + a[i][1].GetDouble();
}
}
BENCHMARK(JSON_CalcSum);
Производтельность, разные
оптимизации, итераций за цикл
Main +
O0
Json +
O0
Main +
O3
Json O3
Main + O3
Json + O0
Main + O0
Json + O3
JSON_ParseInterna
l
7 78 7 78
JSON_ParseExterna
l
8 75 8 75
JSON_CalcSum
301592 4925519 4925519 301592
План
l Дотянуть оптимизацию в разных либах
l Перейти на rapidjson
l Можем добавить шаблоны выражений,
где требуется.
Кирилл
Березин
Ведущий программист
k.berezin@corp.mail.ru

Mais conteúdo relacionado

Mais procurados

«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
it-people
 
Дополненная Реальность в Облаке
Дополненная Реальность в ОблакеДополненная Реальность в Облаке
Дополненная Реальность в Облаке
GeeksLab Odessa
 

Mais procurados (20)

Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Reform: путь к лучшему ORM
Reform: путь к лучшему ORMReform: путь к лучшему ORM
Reform: путь к лучшему ORM
 
OpenACC short review
OpenACC short reviewOpenACC short review
OpenACC short review
 
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловПолухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
введение в Gpu
введение в Gpuвведение в Gpu
введение в Gpu
 
Введение в потоки питона
Введение в потоки питонаВведение в потоки питона
Введение в потоки питона
 
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
 
Haskell
HaskellHaskell
Haskell
 
Дополненная Реальность в Облаке
Дополненная Реальность в ОблакеДополненная Реальность в Облаке
Дополненная Реальность в Облаке
 
Highload: специализированные высокопроизводительные индексы
Highload: специализированные высокопроизводительные индексыHighload: специализированные высокопроизводительные индексы
Highload: специализированные высокопроизводительные индексы
 
JS Fest 2019. Владимир Агафонкин. Быстро по умолчанию: алгоритмическое мышлен...
JS Fest 2019. Владимир Агафонкин. Быстро по умолчанию: алгоритмическое мышлен...JS Fest 2019. Владимир Агафонкин. Быстро по умолчанию: алгоритмическое мышлен...
JS Fest 2019. Владимир Агафонкин. Быстро по умолчанию: алгоритмическое мышлен...
 
Python и Cython
Python и CythonPython и Cython
Python и Cython
 
msumobi2. Лекция 2
msumobi2. Лекция 2msumobi2. Лекция 2
msumobi2. Лекция 2
 
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
 
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
Python AST / Николай Карелин / VPI Development Center [Python Meetup 27.03.15]
 
Почему Kotlin?
Почему Kotlin?Почему Kotlin?
Почему Kotlin?
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 

Semelhante a Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group

Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
Technopark
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
Computer Science Club
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
Andrey Karpov
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
Andrey Karpov
 
Развитие технологий генерации эксплойтов на основе анализа бинарного кода
Развитие технологий генерации эксплойтов на основе анализа бинарного кодаРазвитие технологий генерации эксплойтов на основе анализа бинарного кода
Развитие технологий генерации эксплойтов на основе анализа бинарного кода
Positive Hack Days
 
SAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationSAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentation
Nikolay Samokhvalov
 

Semelhante a Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group (20)

Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 
Народные средства оптимизации PostgreSQL
Народные средства оптимизации PostgreSQLНародные средства оптимизации PostgreSQL
Народные средства оптимизации PostgreSQL
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
 
Построение компилятора на базе LLVM — Павел Сычев
 Построение компилятора на базе LLVM — Павел Сычев Построение компилятора на базе LLVM — Павел Сычев
Построение компилятора на базе LLVM — Павел Сычев
 
Развитие технологий генерации эксплойтов на основе анализа бинарного кода
Развитие технологий генерации эксплойтов на основе анализа бинарного кодаРазвитие технологий генерации эксплойтов на основе анализа бинарного кода
Развитие технологий генерации эксплойтов на основе анализа бинарного кода
 
SAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationSAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentation
 
11 встреча — Введение в GPGPU (А. Свириденков)
11 встреча — Введение в GPGPU (А. Свириденков)11 встреча — Введение в GPGPU (А. Свириденков)
11 встреча — Введение в GPGPU (А. Свириденков)
 
Продолжаем говорить про арифметику
Продолжаем говорить про арифметикуПродолжаем говорить про арифметику
Продолжаем говорить про арифметику
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
C++ exceptions
C++ exceptionsC++ exceptions
C++ exceptions
 
C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...
C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...
C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...
 
Java8. Innovations
Java8. InnovationsJava8. Innovations
Java8. Innovations
 

Mais de Mail.ru Group

AMP для электронной почты, Сергей Пешков
AMP для электронной почты, Сергей ПешковAMP для электронной почты, Сергей Пешков
AMP для электронной почты, Сергей Пешков
Mail.ru Group
 

Mais de Mail.ru Group (20)

Автоматизация без тест-инженеров по автоматизации, Мария Терехина и Владислав...
Автоматизация без тест-инженеров по автоматизации, Мария Терехина и Владислав...Автоматизация без тест-инженеров по автоматизации, Мария Терехина и Владислав...
Автоматизация без тест-инженеров по автоматизации, Мария Терехина и Владислав...
 
BDD для фронтенда. Автоматизация тестирования с Cucumber, Cypress и Jenkins, ...
BDD для фронтенда. Автоматизация тестирования с Cucumber, Cypress и Jenkins, ...BDD для фронтенда. Автоматизация тестирования с Cucumber, Cypress и Jenkins, ...
BDD для фронтенда. Автоматизация тестирования с Cucumber, Cypress и Jenkins, ...
 
Другая сторона баг-баунти-программ: как это выглядит изнутри, Владимир Дубровин
Другая сторона баг-баунти-программ: как это выглядит изнутри, Владимир ДубровинДругая сторона баг-баунти-программ: как это выглядит изнутри, Владимир Дубровин
Другая сторона баг-баунти-программ: как это выглядит изнутри, Владимир Дубровин
 
Использование Fiddler и Charles при тестировании фронтенда проекта pulse.mail...
Использование Fiddler и Charles при тестировании фронтенда проекта pulse.mail...Использование Fiddler и Charles при тестировании фронтенда проекта pulse.mail...
Использование Fiddler и Charles при тестировании фронтенда проекта pulse.mail...
 
Управление инцидентами в Почте Mail.ru, Антон Викторов
Управление инцидентами в Почте Mail.ru, Антон ВикторовУправление инцидентами в Почте Mail.ru, Антон Викторов
Управление инцидентами в Почте Mail.ru, Антон Викторов
 
DAST в CI/CD, Ольга Свиридова
DAST в CI/CD, Ольга СвиридоваDAST в CI/CD, Ольга Свиридова
DAST в CI/CD, Ольга Свиридова
 
Почему вам стоит использовать свой велосипед и почему не стоит Александр Бел...
Почему вам стоит использовать свой велосипед и почему не стоит  Александр Бел...Почему вам стоит использовать свой велосипед и почему не стоит  Александр Бел...
Почему вам стоит использовать свой велосипед и почему не стоит Александр Бел...
 
CV в пайплайне распознавания ценников товаров: трюки и хитрости Николай Масл...
CV в пайплайне распознавания ценников товаров: трюки и хитрости  Николай Масл...CV в пайплайне распознавания ценников товаров: трюки и хитрости  Николай Масл...
CV в пайплайне распознавания ценников товаров: трюки и хитрости Николай Масл...
 
RAPIDS: ускоряем Pandas и scikit-learn на GPU Павел Клеменков, NVidia
RAPIDS: ускоряем Pandas и scikit-learn на GPU  Павел Клеменков, NVidiaRAPIDS: ускоряем Pandas и scikit-learn на GPU  Павел Клеменков, NVidia
RAPIDS: ускоряем Pandas и scikit-learn на GPU Павел Клеменков, NVidia
 
WebAuthn в реальной жизни, Анатолий Остапенко
WebAuthn в реальной жизни, Анатолий ОстапенкоWebAuthn в реальной жизни, Анатолий Остапенко
WebAuthn в реальной жизни, Анатолий Остапенко
 
AMP для электронной почты, Сергей Пешков
AMP для электронной почты, Сергей ПешковAMP для электронной почты, Сергей Пешков
AMP для электронной почты, Сергей Пешков
 
Как мы захотели TWA и сделали его без мобильных разработчиков, Данила Стрелков
Как мы захотели TWA и сделали его без мобильных разработчиков, Данила СтрелковКак мы захотели TWA и сделали его без мобильных разработчиков, Данила Стрелков
Как мы захотели TWA и сделали его без мобильных разработчиков, Данила Стрелков
 
Кейсы использования PWA для партнерских предложений в Delivery Club, Никита Б...
Кейсы использования PWA для партнерских предложений в Delivery Club, Никита Б...Кейсы использования PWA для партнерских предложений в Delivery Club, Никита Б...
Кейсы использования PWA для партнерских предложений в Delivery Club, Никита Б...
 
Метапрограммирование: строим конечный автомат, Сергей Федоров, Яндекс.Такси
Метапрограммирование: строим конечный автомат, Сергей Федоров, Яндекс.ТаксиМетапрограммирование: строим конечный автомат, Сергей Федоров, Яндекс.Такси
Метапрограммирование: строим конечный автомат, Сергей Федоров, Яндекс.Такси
 
Этика искусственного интеллекта, Александр Кармаев (AI Journey)
Этика искусственного интеллекта, Александр Кармаев (AI Journey)Этика искусственного интеллекта, Александр Кармаев (AI Journey)
Этика искусственного интеллекта, Александр Кармаев (AI Journey)
 
Нейро-машинный перевод в вопросно-ответных системах, Федор Федоренко (AI Jour...
Нейро-машинный перевод в вопросно-ответных системах, Федор Федоренко (AI Jour...Нейро-машинный перевод в вопросно-ответных системах, Федор Федоренко (AI Jour...
Нейро-машинный перевод в вопросно-ответных системах, Федор Федоренко (AI Jour...
 
Конвергенция технологий как тренд развития искусственного интеллекта, Владими...
Конвергенция технологий как тренд развития искусственного интеллекта, Владими...Конвергенция технологий как тренд развития искусственного интеллекта, Владими...
Конвергенция технологий как тренд развития искусственного интеллекта, Владими...
 
Обзор трендов рекомендательных систем от Пульса, Андрей Мурашев (AI Journey)
Обзор трендов рекомендательных систем от Пульса, Андрей Мурашев (AI Journey)Обзор трендов рекомендательных систем от Пульса, Андрей Мурашев (AI Journey)
Обзор трендов рекомендательных систем от Пульса, Андрей Мурашев (AI Journey)
 
Мир глазами нейросетей, Данила Байгушев, Александр Сноркин ()
Мир глазами нейросетей, Данила Байгушев, Александр Сноркин ()Мир глазами нейросетей, Данила Байгушев, Александр Сноркин ()
Мир глазами нейросетей, Данила Байгушев, Александр Сноркин ()
 
Learning from Swift sources, Иван Сметанин
Learning from Swift sources, Иван СметанинLearning from Swift sources, Иван Сметанин
Learning from Swift sources, Иван Сметанин
 

Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group

  • 1. Кирилл Березин Ведущий программист Как не сделать врагами архитектуру и оптимизацию 14 Ноября 2019
  • 2. Постановка задачи ● Задача – сменить библиотеку json; ● Json использовался в качестве внутреннего формата данных; ● Использование шаблонов выражений для уменьшения кода.
  • 3. Зачем все это было? ● Исследование показало, что операции с json занимали не меньше 20 % времени; ● Rapidjson read rate 140 МБ/с (худший); ● Cpprest sdk 13 МБ/с.
  • 4. Архитектура программы l Функциональность в библиотеках; l Внутренний формат данных json-lib. dll Json wrapper dll exe
  • 5. Первые результаты l Результат перевода теста одной из библиотек показал: - Rapidjson 15-25 МБ/с - Cpp rest sdk 13 МБ/с
  • 6. Исследуем код cpprest typedef std::vector<std::pair<utility::string_t, json::value>> storage_type;
  • 7. Исследуем код cpprest typedef std::vector<std::pair<utility::string_t, json::value>> storage_type;
  • 8. Исследуем код cpprest l Используется move семантика; l Элементы могут быть сортированы – бинарный поиск; l Элементы полиморфные; l Поставляется в виде отдельной библиотеки.
  • 9. Исследуем код Rapidjson union Data { String s; ShortString ss; ... };
  • 10. Исследуем код Rapidjson l Кастомные аллокаторы; l Микрооптимизации Neon/sse; l header only – уровень оптимизации определяется основным кодом.
  • 11. Сравнительный тест производительности (-O3). l Nativejson benchmark (для некоторого i5 3 поколения) Benchmarking Performance of C++ REST SDK (C++11) Parse canada.json ... 162.880 ms 13.180 MB/s Parse citm_catalog.json ... 23.060 ms 71.431 MB/s Parse twitter.json ... 10.985 ms 54.826 MB/s Benchmarking Performance of RapidJSON_FullPrec (C++) Parse canada.json ... 15.184 ms 141.384 MB/s Parse citm_catalog.json ... 3.109 ms 529.813 MB/s Parse twitter.json ... 2.084 ms 288.992 MB/s
  • 12. Сравнительный тест производительности (-O0). l Nativejson benchmark (для некоторого i5 3 поколения) Benchmarking Performance of C++ REST SDK (C++11) Parse canada.json ... 166.016 ms 12.931 MB/s Parse citm_catalog.json ... 23.549 ms 69.947 MB/s Parse twitter.json ... 11.044 ms 54.533 MB/s Benchmarking Performance of RapidJSON_FullPrec (C++) Parse canada.json ... 139.193 ms 15.423 MB/s Parse citm_catalog.json ... 25.075 ms 65.691 MB/s Parse twitter.json ... 10.639 ms 56.609 MB/s
  • 13. Шаблоны выражений l Быстрая, но громозкая реализация; l Оборачиваем шаблоном, с более короткой реализацией; l Требуется оптимизация кода; l Код по скорости близок к идеальному.
  • 14. template<typename T> struct SArray { explicit SArray(std::size_t s) : storage(new T[s]), storage_size(s) { ... } SArray(SArray<T> const& orig) { … } ~SArray() { delete [] storage; } std::size_t size() const { return storage_size; } T const& operator[] (std::size_t idx) const { return storage[idx]; } T& operator[] (std::size_t idx) { return storage[idx]; } T* storage; std::size_t storage_size; };
  • 15. Низкоуровневая реализация int main() { SArray<double> x(1000), y(1000); for (auto i = 0 ; i < x.size(); ++i) x[i] = 1.2 * x[i] + x[i] * y[i]; }
  • 16. main: push r12 mov edi, 8000 push rbp sub rsp, 8 call operator new[](unsigned long)@PLT lea rdi, 8[rax] ... call operator new[](unsigned long)@PLT lea rdi, 8[rax] ... rep stosq Готовый код конструкторы
  • 17. .L2: movupd xmm3, XMMWORD PTR 0[rbp+rax] movupd xmm0, XMMWORD PTR [r8+rax] mulpd xmm0, xmm3 movapd xmm1, xmm3 mulpd xmm1, xmm2 addpd xmm0, xmm1 movups XMMWORD PTR 0[rbp+rax], xmm0 add rax, 16 cmp rax, 8000 jne .L2 mov rdi, r8 call operator delete[](void*)@PLT mov rdi, rbp call operator delete[](void*)@PLT x[i] = 1.2 * x[i] + x[i] * y[i]; Готовый код выражение.
  • 18. template<typename T, typename OP1, typename OP2> struct A_Mult { typename A_Traits<OP1>::ExprRef op1; // first operand typename A_Traits<OP2>::ExprRef op2; // second operand A_Mult (OP1 const& a, OP2 const& b) : op1(a), op2(b) { } T operator[] (std::size_t idx) const { return op1[idx] * op2[idx]; } std::size_t size() const { return std::max(a.size(), b.size()); } }; Шаблон выражения
  • 19. Скалярный тип. template<typename T> struct A_Scalar { T const& scalar; constexpr A_Scalar (T const& v) : scalar(v) { } constexpr T const& operator[] (std::size_t) const { return scalar; } constexpr std::size_t size() const { return 0; }; };
  • 20. Обертка. template<typename T, typename Rep = SArray<T>> struct Array { Rep expr_rep; // данные или класс-выражение explicit Array (std::size_t s) : expr_rep(s) { } Array (Rep const& rb) : expr_rep(rb) { } Array& operator= (Array const& b) { for (std::size_t idx = 0; idx<b.size(); ++idx) { expr_rep[idx] = b[idx]; } return *this; }
  • 21. std::size_t size() const { return expr_rep.size(); } decltype(auto) operator[] (std::size_t idx) const { return expr_rep[idx]; } T& operator[] (std::size_t idx) { return expr_rep[idx]; } }; Обертка.
  • 22. Оператор template<typename T, typename R1, typename R2> Array<T, A_Mult<T,R1,R2>> operator* (Array<T,R1> const& a, Array<T,R2> const& b) { return Array<T,A_Mult<T,R1,R2>> (A_Mult<T,R1,R2>(a.rep(), b.rep())); }
  • 23. Как используется int main() { Array<double> x(1000), y(1000); x = 1.2 * x + x * y; }
  • 24. Выражение Array<double, SArray<double> >& Array<double, SArray<double> >::operator=<double, A_Add<double, A_Mult<double, A_Scalar<double>, SArray<double> >, A_Mult<double, SArray<double>, SArray<double> > > > ( Array<double, A_Add<double, A_Mult<double, A_Scalar<double>, SArray<double> >, A_Mult<double, SArray<double>, SArray<double> > > > const& ) x = 1.2 * x + x * y;
  • 25. main: ... call operator new[](unsigned long)@PLT ... call operator new[](unsigned long)@PLT ... rep stosq ... movaps XMMWORD PTR 80[rsp], xmm0 call Array<double, ... mov rdi, QWORD PTR 32[rsp] test rdi, rdi je .L46 call operator delete[](void*)@PLT .L46: mov rdi, QWORD PTR 16[rsp] test rdi, rdi je .L47 call operator delete[](void*)@PLT
  • 26. Array<double, SArray<double> >& Array<double, SArray<double> >::operator=<double, A_Add<double, A_Mult<double, A_Scalar<double>, SArray<double> >, A_Mult<double, SArray<double>, SArray<double> > > >(Array<double, A_Add<double, A_Mult<double, A_Scalar<double>, SArray<double> >, A_Mult<double, SArray<double>, SArray<double> > > > const&): push r15 ... .L26: mov r9, QWORD PTR [rdi] mov rsi, QWORD PTR [r12] add rcx, 1 movsd xmm0, QWORD PTR [r9+rdx] mov r9, QWORD PTR 0[rbp] mulsd xmm0, QWORD PTR [rsi+rdx] mov rsi, QWORD PTR [rbx] movsd xmm1, QWORD PTR [r9+rdx] mulsd xmm1, QWORD PTR [rsi] addsd xmm0, xmm1 movsd QWORD PTR [r11], xmm0
  • 27. benchmarks static void Ideal(benchmark::State& state) { SArray<double> x(1000), y(1000); for(auto _ : state) { for (auto i = 0 ; i < x.size(); ++i) x[i] = 1.2 * x[i] + x[i] * y[i]; } } BENCHMARK(Ideal); static void SuperFast(benchmark::State& state) { Array<double> x(1000), y(1000); for(auto _ : state) { x = 1.2 * x + x * y; } } BENCHMARK(SuperFast);
  • 28. Benchmark gcc 9.2.0 (-O3) Run on (8 X 3400 MHz CPU s) CPU Caches: L1 Data 32K (x4) L1 Instruction 32K (x4) L2 Unified 256K (x4) L3 Unified 6144K (x1) Load Average: 0.33, 0.35, 0.21 ----------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------- Ideal 255 ns 255 ns 2693296 SuperFast 346 ns 346 ns 2007497
  • 29. Benchmark gcc 9.2.0 (-O0) Run on (8 X 3400 MHz CPU s) CPU Caches: L1 Data 32K (x4) L1 Instruction 32K (x4) L2 Unified 256K (x4) L3 Unified 6144K (x1) Load Average: 0.33, 0.35, 0.21 ----------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------- Ideal 10123 ns 10119 ns 63845 SuperFast 40989 ns 40971 ns 13674
  • 30. Итог l Rapidjson быстр, но требует оптимизацию l Шаблоны выражений требуют оптимизацию. l Тестовая библиотека дает - Rapidjson 15-25 МБ/с - Cpp rest sdk 13 МБ/с
  • 31. Исследуем проект l Часть библиотек с /Z0 (== -O0) l Часть с включенной оптимизацией
  • 32. Архитектура кода и оптимизация : модуль json.o void readJ(std::string & json) { rapidjson::Document doc; doc.Parse(json.c_str()); } void readJExternal(std::string const& json, rapidjson::Document& doc) { doc.Parse(json.c_str()); }
  • 33. Архитектура кода и оптимизация: модуль main.o static void JSON_ParseInternal(benchmark::State& state) { std::ifstream in_("canada.json"); auto json = std::string(std::istreambuf_iterator<char>(in_), std::istreambuf_iterator<char>()); for(auto _ : state) { readJ(json); } } BENCHMARK(JSON_ParseInternal);
  • 34. Архитектура кода и оптимизация: модуль main.o static void JSON_ParseExternal(benchmark::State& state) { std::ifstream in_("canada.json"); auto json = std::string(std::istreambuf_iterator<char>(in_), std::istreambuf_iterator<char>()); rapidjson::Document doc; for(auto _ : state) { readJExternal(json, doc); } } BENCHMARK(JSON_ParseExternal);
  • 35. Разделение кода и оптимизация: модуль main.o static void JSON_CalcSum(benchmark::State& state) { std::ifstream in_("canada.json"); auto json = std::string(std::istreambuf_iterator<char>(in_), std::istreambuf_iterator<char>()); rapidjson::Document doc; readJExternal(json, doc); for(auto _ : state) { const auto& a = doc["features"][0]["geometry"]["coordinates"][0]; double result = 0.0; for(rapidjson::SizeType i = 0; i < a.Size(); ++i) result += a[i][0].GetDouble() + a[i][1].GetDouble(); } } BENCHMARK(JSON_CalcSum);
  • 36. Производтельность, разные оптимизации, итераций за цикл Main + O0 Json + O0 Main + O3 Json O3 Main + O3 Json + O0 Main + O0 Json + O3 JSON_ParseInterna l 7 78 7 78 JSON_ParseExterna l 8 75 8 75 JSON_CalcSum 301592 4925519 4925519 301592
  • 37. План l Дотянуть оптимизацию в разных либах l Перейти на rapidjson l Можем добавить шаблоны выражений, где требуется.