SlideShare ist ein Scribd-Unternehmen logo
1 von 143
Downloaden Sie, um offline zu lesen
C++ велосипедостроение
для профессионалов
Antony Polukhin
Полухин Антон
Автор Boost библиотек TypeIndex, DLL, Stacktrace
Maintainer Boost Any, Conversion, LexicalСast, Variant
Представитель РГ21, national body
О "велосипедах"
Зачем?
4 / 143
Зачем?
Для самообучения
5 / 143
Зачем?
Для самообучения
Особые требования к коду
6 / 143
Зачем?
Для самообучения
Особые требования к коду
Можем написать лучше
7 / 143
Зачем?
Для самообучения
Особые требования к коду
Можем написать лучше
Кажется что можем написать лучше
8 / 143
Зачем?
Для самообучения
Особые требования к коду
Можем написать лучше
Кажется что можем написать лучше
???
9 / 143
О чём поговорим
Устаревшие технологий
Современные технологии
Оптимизациях и “вредных” бенчмарках
Новейших практиках C++ программирования
Что делать с “идеальным” велосипедом
10 / 143
С какого класса начнём?
std::string
string: c чего всё начиналось
class string {
char* data;
size_t size;
size_t capacity;
// ...
};
13 / 143
string: C++98
class string {
char* data;
// ...
public:
string(const string& s)
: data(s.clone()) // new + memmove
{}
// ...
string(const char* s); // new + memmove
};
14 / 143
string: C++98
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1)
);
15 / 143
string: C++98 проблемы
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // string("Hello!")
);
16 / 143
string: C++98 проблемы
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // make_pair(string("Hello!"), 1)
);
17 / 143
string: C++98 проблемы
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // m.insert(make_pair(string("Hello!"), 1))
);
18 / 143
string: C++98 проблемы
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // (new + memmove) * 2 + delete
); // new + memmove + delete
19 / 143
string: C++98 проблемы
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // копии котрые не надо копировать
); // копии котрые не надо копировать
20 / 143
string: COW
class string_impl {
char* data;
size_t size;
size_t capacity;
size_t use_count; // <===
// ...
};
21 / 143
string: COW
class string {
string_impl* impl;
public:
string(const string& s)
: impl(s.impl)
{
++ impl->use_count;
}
};
22 / 143
string: COW
class string {
string_impl* impl;
public:
char& operator[](size_t i) {
if (impl->use_count > 1)
*this = clone();
}
return impl->data[i];
}
};
23 / 143
string: COW
class string {
string_impl* impl;
public:
~string() {
--impl->use_count;
if (!impl->use_count) {
delete impl;
}
}
};
24 / 143
string: COW
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1)
);
25 / 143
string + COW: C++98
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // string("Hello!")
);
26 / 143
string + COW: C++98
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // make_pair(string("Hello!"), 1)
);
27 / 143
string + COW: C++98
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // m.insert(make_pair(string("Hello!"), 1))
);
28 / 143
string + COW: C++98
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1)
); // 1 new + 1 memmove
29 / 143
COW более чем в 2 раза ускоряет
код в C++98!
Но...
Но... 2002, 2005
2002, 2005
Hyper-threading в процессорах Pentium 4.
2-ядерный процессор Opteron архитектуры AMD64, предназначенный для серверов.
Pentium D x86-64 – первый 2-ядерный процессором для персональных
компьютеров.
33 / 143
string: COW MT fixes
class string_impl {
char* data;
size_t size;
size_t capacity;
size_t use_count; // <=== Ooops!
// ...
};
34 / 143
string: COW MT fixes
class string_impl {
char* data;
size_t size;
size_t capacity;
atomic<size_t> use_count; // <=== Fixed
// ...
};
35 / 143
string: COW MT fixes
class string {
string_impl* impl;
public:
string(const string& s)
: impl(s.impl)
{
++ impl->use_count; // atomic
}
};
36 / 143
string: COW MT fixes
class string {
string_impl* impl;
public:
~string() {
size_t val = --impl->use_count; // atomic
if (!val) {
delete impl;
}
}
};
37 / 143
string: COW MT fixes
class string {
string_impl* impl;
public:
char& operator[](size_t i) {
if (impl->use_count > 1) { // atomic
string cloned = clone(); // new + memmove + atomic --
swap(*this, cloned);
// atomic in ~string for cloned; delete in ~string if other thread released
the string
} // ...
38 / 143
Чем плохи atomic?
Не атомарный INC – 1 такт [1]
[1] http://www.agner.org/optimize/instruction_tables.pdf
39 / 143
Чем плохи atomic?
Не атомарный INC – 1 такт [1]
Атомарная операция на одном ядре [2]:
~5 - 20+ тактов, если это ядро "владеет" атомиком
[1] http://www.agner.org/optimize/instruction_tables.pdf
[2] https://htor.inf.ethz.ch/publications/img/atomic-bench.pdf
40 / 143
Чем плохи atomic?
Не атомарный INC – 1 такт [1]
Атомарная операция на одном ядре [2]:
~5 - 20+ тактов, если это ядро "владеет" атомиком
~40 тактов, если другое ядро "владеет" атомиком
[1] http://www.agner.org/optimize/instruction_tables.pdf
[2] https://htor.inf.ethz.ch/publications/img/atomic-bench.pdf
41 / 143
Чем плохи atomic? (часть 1)
Не атомарный INC – 1 такт [1]
Атомарная операция на одном ядре [2]:
~5 - 20+ тактов, если это ядро "владеет" атомиком
~40 тактов, если другое ядро "владеет" атомиком
Несколько атомарных операций на разных ядрах над одним atomic:
retry later [3]
[1] http://www.agner.org/optimize/instruction_tables.pdf
[2] https://htor.inf.ethz.ch/publications/img/atomic-bench.pdf
[3] https://en.wikipedia.org/wiki/MESI_protocol
42 / 143
atomic (часть 1, макс. простой ядра)
43 / 143
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0
100
200
300
400
500
600
700
# cores
maxcoredelay
atomic (часть 1, суммарный простой ядер)
44 / 143
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0
1000
2000
3000
4000
5000
6000
# cores
Totaldelay
Чем плохи atomic (часть 2)
Компиляторы не очень умеют их оптимизировать.
Full barrier для некоторых компиляторов.
Накладывают дополнительные ограничения на близлежащие оптимизации.
https://gcc.gnu.org/wiki/Atomic/GCCMM/Optimizations
http://llvm.org/docs/Atomics.html
45 / 143
COW более чем в 2 раза ускоряет
код в C++98!
С++11
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
string::string(string&& s) {
swap(*this, s);
}
48 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // string("Hello!")
);
49 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // pair(string&&, int)
);
50 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1) // m.insert(pair&&)
);
51 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1)
); // 1 new + 1 memmove
52 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
В C++11 если мы копируем объект, то скорее всего мы его будем менять (в
остальных случаях rvalue + RVO + NRVO)
53 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
В C++11 если мы копируем объект, то скорее всего мы его будем менять (в
остальных случаях rvalue + RVO + NRVO)
Без COW: new + memmove
COW: atomic + atomic(x?) + new + memmove
54 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
В C++11 если мы копируем объект, то скорее всего мы его будем менять (в
остальных случаях rvalue + RVO + NRVO)
COW вызывает atomic удручающе часто на некоторых операциях
char& operator[](size_t i) {
if (impl->use_count > 1) { // atomic
string cloned = clone(); // atomic--
swap(*this, cloned); // atomic in ~string for cloned
}
return impl->data[i];
}
55 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
В C++11 если мы копируем объект, то скорее всего мы его будем менять (в
остальных случаях rvalue + RVO + NRVO)
COW вызывает atomic удручающе часто на некоторых операциях
COW конструктор копирования использует atomic
COW деструктор COW строки вызывает atomic
56 / 143
string: С++11 and COW
Временные объекты и без COW оптимизируются в C++11
В C++11 если мы копируем объект, то скорее всего мы его будем менять (в
остальных случаях rvalue + RVO + NRVO)
COW вызывает atomic удручающе часто на некоторых операциях
COW конструктор копирования использует atomic
COW деструктор COW строки вызывает atomic
Итого:
С COW мы получаем множество дополнительных операций над атомиками
57 / 143
COW устарел!
Осторожнее с использованием.
COW legacy
class bimap {
std::map<string, int> str_to_int;
std::map<int, string> int_to_str;
public:
void insert(string s, int i) {
str_to_int.insert(make_pair(s, i));
int_to_str.insert(make_pair(i, std::move(s)));
}
59 / 143
COW legacy
class bimap {
std::map<string, int> str_to_int;
std::map<int, string> int_to_str;
public:
void insert(string s, int i) {
str_to_int.insert(make_pair(s, i));
int_to_str.insert(make_pair(i, std::move(s)));
}
60 / 143
COW legacy
class bimap {
std::map<string, int> str_to_int;
std::map<int, string> int_to_str;
public:
void insert(string s, int i) {
str_to_int.insert(make_pair(s, i));
int_to_str.insert(make_pair(i, std::move(s)));
}
61 / 143
COW legacy fix
class bimap {
std::map<string, int> str_to_int;
std::map<int, string_view> int_to_str;
public:
void insert(string s, int i) {
auto it = str_to_int.insert(make_pair(std::move(s), i));
int_to_str.insert(make_pair(i, *it.first)));
}
62 / 143
Одна оптимизация для string
Одна оптимизация для string
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1)
); // 1 new + 1 memmove
64 / 143
Одна оптимизация для string
class string {
char* data;
size_t size;
size_t capacity;
// ...
};
65 / 143
Одна оптимизация для string
class string {
size_t capacity;
union {
struct no_small_buffer_t {
char* data;
size_t size;
} no_small_buffer;
struct small_buffer_t {
char data[sizeof(no_small_buffer_t)];
} small_buffer;
} impl;
66 / 143
Одна оптимизация для string
class string {
size_t capacity;
union {
struct no_small_buffer_t {
char* data;
size_t size;
} no_small_buffer;
struct small_buffer_t {
char data[sizeof(no_small_buffer_t)];
} small_buffer;
} impl;
67 / 143
Одна оптимизация для string
class string {
size_t capacity; // == 0
union {
struct no_small_buffer_t {
char* data;
size_t size;
} no_small_buffer;
struct small_buffer_t {
char data[sizeof(no_small_buffer_t)];
} small_buffer;
} impl;
68 / 143
Одна оптимизация для string
class string {
size_t capacity; // == 0
union {
struct no_small_buffer_t {
char* data;
size_t size;
} no_small_buffer;
struct small_buffer_t {
char data[sizeof(no_small_buffer_t)]; // <=======
} small_buffer;
} impl;
69 / 143
Одна оптимизация для string
class string {
size_t capacity; // != 0
union {
struct no_small_buffer_t {
char* data;
size_t size;
} no_small_buffer;
struct small_buffer_t {
char data[sizeof(no_small_buffer_t)];
} small_buffer;
} impl;
70 / 143
Одна оптимизация для string
class string {
size_t capacity; // != 0
union {
struct no_small_buffer_t {
char* data; // <=======
size_t size;
} no_small_buffer;
struct small_buffer_t {
char data[sizeof(no_small_buffer_t)];
} small_buffer;
} impl;
71 / 143
Одна оптимизация для string
std::map<string, int> m;
m.insert(
std::pair<string, int>("Hello!", 1)
); // 1 memmove
72 / 143
Разработка без оглядки на
готовые решения
std::variant
std::variant<T...> - класс который хранит один из типов T, и помнит тип данных:
union {
T0 v0;
T1 v1;
T2 v2;
// ...
};
int t_index;
74 / 143
Кто видит ошибку?
template <class... T>
class variant {
// ...
std::tuple<T...> data;
};
75 / 143
Кто видит ошибку?
variant<T...>& operator=(const U& value) {
if (&value == this) {
return *this;
}
destroy(); // data->~Ti()
construct_value(value); // new (data) Tj(value);
t_index = get_index_from_type<U>(); // t_index = j;
return *this;
}
76 / 143
Кто видит ошибку?
variant<T...>& operator=(const U& value) {
if (&value == this) {
return *this;
}
destroy(); // data->~Ti()
construct_value(value); // throw; ...
// ...
// ~variant() {
// data->~Ti() // Oops!!!
77 / 143
FORCE INLINE
У процессора есть не только кеш данных
Большинство современных микропроцессоров для компьютеров и серверов имеют
как минимум три независимых кэша: кэш инструкций для ускорения загрузки
машинного кода, кэш данных для ускорения чтения и записи данных и буфер
ассоциативной трансляции (TLB) для ускорения трансляции виртуальных
(логических) адресов в физические, как для инструкций, так и для данных. [1]
[1] https://ru.wikipedia.org/wiki/Кэш_процессора
79 / 143
Кеш инструкций здоровой программы
Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
ab illo inventore veritatis et quasi architecto beatae vitae dicta
sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,
aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit,
sed quia non numquam eius modi tempora incidunt, ut labore et dolore
magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis
nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut
aliquid ex ea commodi consequatur?
80 / 143
Кеш инструкций с FORCE_INLINE
Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totFORCE_INLINE rem
aperiFORCE_INLINE eaque ipsa, quae ab illo inventore veritatis et
quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim
ipsFORCE_INLINE voluptatem, quia voluptas sit, aspernatur aut odit aut
fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem
sequi nesciunt, neque porro quisquFORCE_INLINE est, qui dolorem ipsum,
quia dolor sit, FORCE_INLINEet, consectetur, adipisci velit, sed quia
non numquFORCE_INLINE eius modi tempora incidunt, ut labore et dolore
magnFORCE_INLINE aliquFORCE_INLINE quaerat voluptatem. Ut enim ad
minima veniFORCE_INLINE, quis nostrum exercitationem ullFORCE_INLINE
corporis suscipit laboriosFORCE_INLINE, nisi ut aliquid ex ea commodi
consequatur?
81 / 143
Много бенчмарков игнорируют 2 кеша из 3х
Бенчмарк должен:
в бинарном виде занимать столько же, сколько ваш production код
иметь приблизительно такое же количество функций и переходов, как и ваш
production код
82 / 143
True story
Убрав часть шаблонных параметров из B-Tree и ощутимо уменьшив размер
бинарника, получили двукратный прирост скорости.
83 / 143
Мнение эксперта
Understanding Compiler Optimization - Chandler Carrut: ”To get an interesting example
<on inlining that hurts performance> it has to be big <...>”[1]
"Tuning C++: Benchmarks, and CPUs, and Compilers! Oh My! - Chandler Carrut:
“Primary reason why modern processors are slow is due to cache misses. <...> One of
those caches is actually a cache that feeds your program to the CPU. <...> When you
skip over instructions, you may skip over the cache line <...> and you stall” [2]
[1] https://www.youtube.com/watch?v=FnGCDLhaxKU
[2] https://www.youtube.com/watch?v=nXaxk27zwlk
84 / 143
Aliasing
Aliasing
Можно встретить рекомендации:
использовать -fno-strict-aliasing чтобы получать меньше проблем.
86 / 143
Aliasing
bar qwe(foo& f, const bar& b) {
f += b;
return b * b;
}
87 / 143
Aliasing
bar qwe(foo& f, const bar& b) {
// load b
// load f
f += b;
// load b
return b * b;
}
88 / 143
Aliasing
bar qwe(foo& f, const bar& b) {
// load b
// load f
f += b; // &f == &b ???
// load b
return b * b;
}
89 / 143
Aliasing
5% - 30% Speedup
http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1124&context=ecetr
https://habrahabr.ru/post/114117/
90 / 143
Советы на каждый день
vector
auto foo() {
std::vector< std::shared_ptr<int> > res;
for (unsigned i = 0; i < 1000; ++i)
res.push_back(
std::shared_ptr<int>(new int)
);
return res;
}
92 / 143
vector
auto foo() {
std::vector< std::shared_ptr<int> > res;
res.reserve(1000); // <======
for (unsigned i = 0; i < 1000; ++i)
res.push_back(
std::shared_ptr<int>(new int)
);
return res;
}
93 / 143
make_shared
auto foo() {
std::vector< std::shared_ptr<int> > res;
res.reserve(1000);
for (unsigned i = 0; i < 1000; ++i)
res.push_back(
std::make_shared<int>() // <======
);
return res;
}
94 / 143
for (auto v: data)
auto foo() {
std::vector<std::string> data = get_data();
// ...
for (auto v: data)
res.insert(
v
);
return res;
}
95 / 143
for (auto v: data)
auto foo() {
std::vector<std::string> data = get_data();
// ...
for (auto v: data)
res.insert(
std::move(v) // <======
);
return res;
}
96 / 143
for (auto&& v: data)
auto foo() {
std::vector<std::string> data = get_data();
// ...
for (auto&& v: data) // <======
res.insert(
std::move(v)
);
return res;
}
97 / 143
vector
auto foo() {
std::vector<std::string> data = get_data();
// ...
std::string res = data.back();
data.pop_back();
// ...
return res;
}
98 / 143
vector
auto foo() {
std::vector<std::string> data = get_data();
// ...
std::string res = std::move(data.back()); // <======
data.pop_back();
// ...
return res;
}
99 / 143
Global constants
#include <vector>
#include <string>
static const std::vector<std::string> CONSTANTS = {
"Hello",
"Word",
"!"
};
100 / 143
Global constants
#include <string_view>
static const std::string_view CONSTANTS[] = {
"Hello",
"Word",
"!"
};
101 / 143
Global constants
#include <string_view>
constexpr std::string_view CONSTANTS[] = {
"Hello",
"Word",
"!"
};
102 / 143
Inheritance
// base.hpp
struct base {
// ...
virtual void act() = 0;
virtual ~base(){}
};
103 / 143
Inheritance
// something.cpp
struct some_implementation: base {
// ...
void act() { /* ... */ }
};
104 / 143
Inheritance
// something.cpp
struct some_implementation: base {
// ...
void act() override { /* ... */ } // <======
};
105 / 143
Inheritance
// something.cpp
struct some_implementation final: base { // <======
// ...
void act() override { /* ... */ }
};
106 / 143
Inheritance
// something.cpp
namespace { // <======
struct some_implementation final: base {
// ...
void act() override { /* ... */ }
};
} // anonymous namespace
107 / 143
Move constructors
class my_data_struct {
std::vector<int> data_;
public:
// ...
my_data_struct(my_data_struct&& other)
: data_(other.data_)
{}
};
108 / 143
Move constructors
class my_data_struct {
std::vector<int> data_;
public:
// ...
my_data_struct(my_data_struct&& other)
: data_(std::move(other.data_)) // <======
{}
};
109 / 143
Move constructors
class my_data_struct {
std::vector<int> data_;
public:
// ...
my_data_struct(my_data_struct&& other) noexcept // <======
: data_(std::move(other.data_))
{}
};
110 / 143
Move constructors
class my_data_struct {
std::vector<int> data_;
public:
// ...
my_data_struct(my_data_struct&& ) = default; // <======
};
111 / 143
Caching
Caching divs
// .h
extern const double fractions[256];
// .cpp
const double fractions[256] = { 1.0 / i, ... };
113 / 143
Caching divs
2 cycles - MOV r32/64,m
=> 5 cycles - L1 Data Cache Latency for complex address calcs (p[n])
12 cycles - L2 Cache Latency
=> 22-29 cycles - DIV r32
36-43 cycles - L3 Cache Latency
114 / 143
Caching divs
5 cycles vs 22-29 cycles
115 / 143
Caching divs
5 cycles vs 22-29 cycles
x2-x4 faster
116 / 143
Caching divs
5 cycles vs 22-29 cycles
x2-x4 faster
WOW!!!
117 / 143
Caching divs
5 cycles vs 22-29 cycles
x2-x4 faster
WOW!!!
Чё правда?
118 / 143
Caching divs (недостатки)
Оптимизатор не видит значения
119 / 143
Caching divs (недостатки)
Оптимизатор не видит значения:
нет возможности оптимизировать ближайшие инструкции
120 / 143
Caching divs (недостатки)
Оптимизатор не видит значения:
нет возможности оптимизировать ближайшие инструкции
нет возможности constexpr
121 / 143
Caching divs (недостатки)
Оптимизатор не видит значения:
нет возможности оптимизировать ближайшие инструкции
нет возможности constexpr
aliasing (ссылка на double может указывать на ту же ячейку памяти)
122 / 143
Caching divs (недостатки)
Серьёзное негативное влияние на производительность:
нет возможности оптимизировать ближайшие инструкции
нет возможности constexpr
aliasing (ссылка на double может указывать на ту же ячейку памяти)
123 / 143
Caching divs (недостатки)
Серьёзное негативное влияние на производительность:
нет возможности оптимизировать ближайшие инструкции
нет возможности constexpr
aliasing (ссылка на double может указывать на ту же ячейку памяти)
Мы потратили кучу времени на обсуждение этого...
124 / 143
Caching divs (недостатки)
Серьёзное негативное влияние на производительность:
нет возможности оптимизировать ближайшие инструкции
нет возможности constexpr
aliasing (ссылка на double может указывать на ту же ячейку памяти)
Мы потратили кучу времени на обсуждение этого...
Потратили 1Kb L1 кеша из 16/32Kb
111 / 143
Caching divs (недостатки)
Потратили 1Kb L1 кеша из 16/32Kb
"Зачастую, производительность системы ограничена не скоростью
процессора, а производительностью подсистем памяти. В то время, как
традиционно компиляторы уделяли большее внимание оптимизации работы
процессора, сегодня больший упор следует делать на более эффективное
использование иерархии памяти." [1]
[1] Ахо, Сети, Ульман. Компиляторы. Принципы, технологии, инструменты. 2ed.2008
126 / 143
Caching divs (недостатки)
Потратили 1Kb L1 кеша из 16/32Kb
x10 прирост производительности без изменения инструкций [1] => Кеш очень
важен
[1] Ulrich Drepper. What Every Programmer Should Know About Memory. 2007
127 / 143
Идаельный велосипед
Велосипед должен быть
Кроссплатформенный
129 / 143
Велосипед должен быть
Кроссплатформенный
Быстрый
130 / 143
Велосипед должен быть
Кроссплатформенный
Быстрый
Полезный
143 / 143
Велосипед должен быть
Кроссплатформенный
Быстрый
Полезный
Функциональный
132 / 143
РГ21
https://stdcpp.ru/proposals
133 / 143
РГ21
134 / 143
https://stdcpp.ru/proposals
Поможем с формальностями
Поможем с принятием в Boost
Поможем с написанием proposal
Защитим ваши интересы на заседнии, передадим отзывы
РГ21. Идеи в разработке:
135 / 143
РГ21. Идеи в разработке:
136 / 143
P0539R0: wide_int by Игорь Клеванец
РГ21. Идеи в разработке:
137 / 143
P0539R0: wide_int by Игорь Клеванец
P0275R1: Dynamic library load
РГ21. Идеи в разработке:
138 / 143
P0539R0: wide_int by Игорь Клеванец
P0275R1: Dynamic library load
ConcurrentHashMap by Сергей Мурылев, Антон Малахов
РГ21. Идеи в разработке:
139 / 143
P0539R0: wide_int by Игорь Клеванец
P0275R1: Dynamic library load
ConcurrentHashMap by Сергей Мурылев, Антон Малахов
Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, …
РГ21. Идеи в разработке:
140 / 143
P0539R0: wide_int by Игорь Клеванец
P0275R1: Dynamic library load
ConcurrentHashMap by Сергей Мурылев, Антон Малахов
Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, …
?dlsym?
РГ21. Идеи в разработке:
141 / 143
P0539R0: wide_int by Игорь Клеванец
P0275R1: Dynamic library load
ConcurrentHashMap by Сергей Мурылев, Антон Малахов
Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, …
?dlsym?
Stacktrace
РГ21. Идеи в разработке:
142 / 143
P0539R0: wide_int by Игорь Клеванец
P0275R1: Dynamic library load
ConcurrentHashMap by Сергей Мурылев, Антон Малахов
Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, …
?dlsym?
Stacktrace
100500 мелочей, которые делают все в ISO WG21
Спасибо! Вопросы?
https://stdcpp.ru/
143 / 143

Weitere ähnliche Inhalte

Was ist angesagt?

Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработки
victor-yastrebov
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Platonov Sergey
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Mikhail Kurnosov
 

Was ist angesagt? (20)

Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработки
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
Павел Довгалюк, Обратная отладка
Павел Довгалюк, Обратная отладкаПавел Довгалюк, Обратная отладка
Павел Довгалюк, Обратная отладка
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++
 
хитрости выведения типов
хитрости выведения типовхитрости выведения типов
хитрости выведения типов
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
 
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
 
Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.
Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.
Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.
 
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 

Ähnlich wie Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов

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
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
Alex Tumanoff
 

Ähnlich wie Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов (20)

Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
 
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
 
Cpp0x Introduction
Cpp0x IntroductionCpp0x Introduction
Cpp0x Introduction
 
Про асинхронное сетевое программирование
Про асинхронное сетевое программированиеПро асинхронное сетевое программирование
Про асинхронное сетевое программирование
 
Programming c++ (begin-if-else)
Programming c++ (begin-if-else)Programming c++ (begin-if-else)
Programming c++ (begin-if-else)
 
Erlang
ErlangErlang
Erlang
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатно
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Кодогенерация на службе оптимизации, Игорь Чевдарь, СКБ Контур
 Кодогенерация на службе оптимизации, Игорь Чевдарь, СКБ Контур  Кодогенерация на службе оптимизации, Игорь Чевдарь, СКБ Контур
Кодогенерация на службе оптимизации, Игорь Чевдарь, СКБ Контур
 
Intro to Swift techitout
Intro to Swift techitoutIntro to Swift techitout
Intro to Swift techitout
 
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
 
Coroutines
CoroutinesCoroutines
Coroutines
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
Объектно-Ориентированное Программирование на C++, Лекции  3 и 4 Объектно-Ориентированное Программирование на C++, Лекции  3 и 4
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложений
 
Олесь Писаренко "Открываем Яндекс.Танк"
Олесь Писаренко "Открываем Яндекс.Танк"Олесь Писаренко "Открываем Яндекс.Танк"
Олесь Писаренко "Открываем Яндекс.Танк"
 
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
C++ CoreHard Autumn 2018. Полезный constexpr - Антон ПолухинC++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 

Mehr von Sergey Platonov

Mehr von Sergey Platonov (20)

Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++Next
 
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itEvgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
 
Василий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейВасилий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексией
 
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного багаЛев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
 
Антон Бикинеев, Writing good std::future&lt; C++ >
Антон Бикинеев, Writing good std::future&lt; C++ >Антон Бикинеев, Writing good std::future&lt; C++ >
Антон Бикинеев, Writing good std::future&lt; C++ >
 
Павел Филонов, Разделяй и управляй вместе с Conan.io
Павел Филонов, Разделяй и управляй вместе с Conan.ioПавел Филонов, Разделяй и управляй вместе с Conan.io
Павел Филонов, Разделяй и управляй вместе с Conan.io
 
Антон Полухин. C++17
Антон Полухин. C++17Антон Полухин. C++17
Антон Полухин. C++17
 
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на QtДенис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
 
Дмитрий Нестерук, Паттерны проектирования в XXI веке
Дмитрий Нестерук, Паттерны проектирования в XXI векеДмитрий Нестерук, Паттерны проектирования в XXI веке
Дмитрий Нестерук, Паттерны проектирования в XXI веке
 
Dori Exterman, Considerations for choosing the parallel computing strategy th...
Dori Exterman, Considerations for choosing the parallel computing strategy th...Dori Exterman, Considerations for choosing the parallel computing strategy th...
Dori Exterman, Considerations for choosing the parallel computing strategy th...
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
 
Антон Нонко, Классические строки в C++
Антон Нонко, Классические строки в C++Антон Нонко, Классические строки в C++
Антон Нонко, Классические строки в C++
 
Михаил Матросов, Повседневный С++: boost и STL
Михаил Матросов, Повседневный С++: boost и STLМихаил Матросов, Повседневный С++: boost и STL
Михаил Матросов, Повседневный С++: boost и STL
 
Алексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляАлексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуля
 
Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++
 
Дмитрий Демчук. Кроссплатформенный краш-репорт
Дмитрий Демчук. Кроссплатформенный краш-репортДмитрий Демчук. Кроссплатформенный краш-репорт
Дмитрий Демчук. Кроссплатформенный краш-репорт
 
Илья Шишков, Принципы создания тестируемого кода
Илья Шишков, Принципы создания тестируемого кодаИлья Шишков, Принципы создания тестируемого кода
Илья Шишков, Принципы создания тестируемого кода
 
Антон Наумович, Система автоматической крэш-аналитики своими средствами
Антон Наумович, Система автоматической крэш-аналитики своими средствамиАнтон Наумович, Система автоматической крэш-аналитики своими средствами
Антон Наумович, Система автоматической крэш-аналитики своими средствами
 

Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов

  • 1.
  • 2. C++ велосипедостроение для профессионалов Antony Polukhin Полухин Антон Автор Boost библиотек TypeIndex, DLL, Stacktrace Maintainer Boost Any, Conversion, LexicalСast, Variant Представитель РГ21, national body
  • 7. Зачем? Для самообучения Особые требования к коду Можем написать лучше 7 / 143
  • 8. Зачем? Для самообучения Особые требования к коду Можем написать лучше Кажется что можем написать лучше 8 / 143
  • 9. Зачем? Для самообучения Особые требования к коду Можем написать лучше Кажется что можем написать лучше ??? 9 / 143
  • 10. О чём поговорим Устаревшие технологий Современные технологии Оптимизациях и “вредных” бенчмарках Новейших практиках C++ программирования Что делать с “идеальным” велосипедом 10 / 143
  • 13. string: c чего всё начиналось class string { char* data; size_t size; size_t capacity; // ... }; 13 / 143
  • 14. string: C++98 class string { char* data; // ... public: string(const string& s) : data(s.clone()) // new + memmove {} // ... string(const char* s); // new + memmove }; 14 / 143
  • 15. string: C++98 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) ); 15 / 143
  • 16. string: C++98 проблемы std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // string("Hello!") ); 16 / 143
  • 17. string: C++98 проблемы std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // make_pair(string("Hello!"), 1) ); 17 / 143
  • 18. string: C++98 проблемы std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // m.insert(make_pair(string("Hello!"), 1)) ); 18 / 143
  • 19. string: C++98 проблемы std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // (new + memmove) * 2 + delete ); // new + memmove + delete 19 / 143
  • 20. string: C++98 проблемы std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // копии котрые не надо копировать ); // копии котрые не надо копировать 20 / 143
  • 21. string: COW class string_impl { char* data; size_t size; size_t capacity; size_t use_count; // <=== // ... }; 21 / 143
  • 22. string: COW class string { string_impl* impl; public: string(const string& s) : impl(s.impl) { ++ impl->use_count; } }; 22 / 143
  • 23. string: COW class string { string_impl* impl; public: char& operator[](size_t i) { if (impl->use_count > 1) *this = clone(); } return impl->data[i]; } }; 23 / 143
  • 24. string: COW class string { string_impl* impl; public: ~string() { --impl->use_count; if (!impl->use_count) { delete impl; } } }; 24 / 143
  • 25. string: COW std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) ); 25 / 143
  • 26. string + COW: C++98 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // string("Hello!") ); 26 / 143
  • 27. string + COW: C++98 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // make_pair(string("Hello!"), 1) ); 27 / 143
  • 28. string + COW: C++98 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // m.insert(make_pair(string("Hello!"), 1)) ); 28 / 143
  • 29. string + COW: C++98 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) ); // 1 new + 1 memmove 29 / 143
  • 30. COW более чем в 2 раза ускоряет код в C++98!
  • 33. 2002, 2005 Hyper-threading в процессорах Pentium 4. 2-ядерный процессор Opteron архитектуры AMD64, предназначенный для серверов. Pentium D x86-64 – первый 2-ядерный процессором для персональных компьютеров. 33 / 143
  • 34. string: COW MT fixes class string_impl { char* data; size_t size; size_t capacity; size_t use_count; // <=== Ooops! // ... }; 34 / 143
  • 35. string: COW MT fixes class string_impl { char* data; size_t size; size_t capacity; atomic<size_t> use_count; // <=== Fixed // ... }; 35 / 143
  • 36. string: COW MT fixes class string { string_impl* impl; public: string(const string& s) : impl(s.impl) { ++ impl->use_count; // atomic } }; 36 / 143
  • 37. string: COW MT fixes class string { string_impl* impl; public: ~string() { size_t val = --impl->use_count; // atomic if (!val) { delete impl; } } }; 37 / 143
  • 38. string: COW MT fixes class string { string_impl* impl; public: char& operator[](size_t i) { if (impl->use_count > 1) { // atomic string cloned = clone(); // new + memmove + atomic -- swap(*this, cloned); // atomic in ~string for cloned; delete in ~string if other thread released the string } // ... 38 / 143
  • 39. Чем плохи atomic? Не атомарный INC – 1 такт [1] [1] http://www.agner.org/optimize/instruction_tables.pdf 39 / 143
  • 40. Чем плохи atomic? Не атомарный INC – 1 такт [1] Атомарная операция на одном ядре [2]: ~5 - 20+ тактов, если это ядро "владеет" атомиком [1] http://www.agner.org/optimize/instruction_tables.pdf [2] https://htor.inf.ethz.ch/publications/img/atomic-bench.pdf 40 / 143
  • 41. Чем плохи atomic? Не атомарный INC – 1 такт [1] Атомарная операция на одном ядре [2]: ~5 - 20+ тактов, если это ядро "владеет" атомиком ~40 тактов, если другое ядро "владеет" атомиком [1] http://www.agner.org/optimize/instruction_tables.pdf [2] https://htor.inf.ethz.ch/publications/img/atomic-bench.pdf 41 / 143
  • 42. Чем плохи atomic? (часть 1) Не атомарный INC – 1 такт [1] Атомарная операция на одном ядре [2]: ~5 - 20+ тактов, если это ядро "владеет" атомиком ~40 тактов, если другое ядро "владеет" атомиком Несколько атомарных операций на разных ядрах над одним atomic: retry later [3] [1] http://www.agner.org/optimize/instruction_tables.pdf [2] https://htor.inf.ethz.ch/publications/img/atomic-bench.pdf [3] https://en.wikipedia.org/wiki/MESI_protocol 42 / 143
  • 43. atomic (часть 1, макс. простой ядра) 43 / 143 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 100 200 300 400 500 600 700 # cores maxcoredelay
  • 44. atomic (часть 1, суммарный простой ядер) 44 / 143 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 1000 2000 3000 4000 5000 6000 # cores Totaldelay
  • 45. Чем плохи atomic (часть 2) Компиляторы не очень умеют их оптимизировать. Full barrier для некоторых компиляторов. Накладывают дополнительные ограничения на близлежащие оптимизации. https://gcc.gnu.org/wiki/Atomic/GCCMM/Optimizations http://llvm.org/docs/Atomics.html 45 / 143
  • 46. COW более чем в 2 раза ускоряет код в C++98!
  • 48. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 string::string(string&& s) { swap(*this, s); } 48 / 143
  • 49. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // string("Hello!") ); 49 / 143
  • 50. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // pair(string&&, int) ); 50 / 143
  • 51. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) // m.insert(pair&&) ); 51 / 143
  • 52. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) ); // 1 new + 1 memmove 52 / 143
  • 53. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 В C++11 если мы копируем объект, то скорее всего мы его будем менять (в остальных случаях rvalue + RVO + NRVO) 53 / 143
  • 54. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 В C++11 если мы копируем объект, то скорее всего мы его будем менять (в остальных случаях rvalue + RVO + NRVO) Без COW: new + memmove COW: atomic + atomic(x?) + new + memmove 54 / 143
  • 55. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 В C++11 если мы копируем объект, то скорее всего мы его будем менять (в остальных случаях rvalue + RVO + NRVO) COW вызывает atomic удручающе часто на некоторых операциях char& operator[](size_t i) { if (impl->use_count > 1) { // atomic string cloned = clone(); // atomic-- swap(*this, cloned); // atomic in ~string for cloned } return impl->data[i]; } 55 / 143
  • 56. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 В C++11 если мы копируем объект, то скорее всего мы его будем менять (в остальных случаях rvalue + RVO + NRVO) COW вызывает atomic удручающе часто на некоторых операциях COW конструктор копирования использует atomic COW деструктор COW строки вызывает atomic 56 / 143
  • 57. string: С++11 and COW Временные объекты и без COW оптимизируются в C++11 В C++11 если мы копируем объект, то скорее всего мы его будем менять (в остальных случаях rvalue + RVO + NRVO) COW вызывает atomic удручающе часто на некоторых операциях COW конструктор копирования использует atomic COW деструктор COW строки вызывает atomic Итого: С COW мы получаем множество дополнительных операций над атомиками 57 / 143
  • 58. COW устарел! Осторожнее с использованием.
  • 59. COW legacy class bimap { std::map<string, int> str_to_int; std::map<int, string> int_to_str; public: void insert(string s, int i) { str_to_int.insert(make_pair(s, i)); int_to_str.insert(make_pair(i, std::move(s))); } 59 / 143
  • 60. COW legacy class bimap { std::map<string, int> str_to_int; std::map<int, string> int_to_str; public: void insert(string s, int i) { str_to_int.insert(make_pair(s, i)); int_to_str.insert(make_pair(i, std::move(s))); } 60 / 143
  • 61. COW legacy class bimap { std::map<string, int> str_to_int; std::map<int, string> int_to_str; public: void insert(string s, int i) { str_to_int.insert(make_pair(s, i)); int_to_str.insert(make_pair(i, std::move(s))); } 61 / 143
  • 62. COW legacy fix class bimap { std::map<string, int> str_to_int; std::map<int, string_view> int_to_str; public: void insert(string s, int i) { auto it = str_to_int.insert(make_pair(std::move(s), i)); int_to_str.insert(make_pair(i, *it.first))); } 62 / 143
  • 64. Одна оптимизация для string std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) ); // 1 new + 1 memmove 64 / 143
  • 65. Одна оптимизация для string class string { char* data; size_t size; size_t capacity; // ... }; 65 / 143
  • 66. Одна оптимизация для string class string { size_t capacity; union { struct no_small_buffer_t { char* data; size_t size; } no_small_buffer; struct small_buffer_t { char data[sizeof(no_small_buffer_t)]; } small_buffer; } impl; 66 / 143
  • 67. Одна оптимизация для string class string { size_t capacity; union { struct no_small_buffer_t { char* data; size_t size; } no_small_buffer; struct small_buffer_t { char data[sizeof(no_small_buffer_t)]; } small_buffer; } impl; 67 / 143
  • 68. Одна оптимизация для string class string { size_t capacity; // == 0 union { struct no_small_buffer_t { char* data; size_t size; } no_small_buffer; struct small_buffer_t { char data[sizeof(no_small_buffer_t)]; } small_buffer; } impl; 68 / 143
  • 69. Одна оптимизация для string class string { size_t capacity; // == 0 union { struct no_small_buffer_t { char* data; size_t size; } no_small_buffer; struct small_buffer_t { char data[sizeof(no_small_buffer_t)]; // <======= } small_buffer; } impl; 69 / 143
  • 70. Одна оптимизация для string class string { size_t capacity; // != 0 union { struct no_small_buffer_t { char* data; size_t size; } no_small_buffer; struct small_buffer_t { char data[sizeof(no_small_buffer_t)]; } small_buffer; } impl; 70 / 143
  • 71. Одна оптимизация для string class string { size_t capacity; // != 0 union { struct no_small_buffer_t { char* data; // <======= size_t size; } no_small_buffer; struct small_buffer_t { char data[sizeof(no_small_buffer_t)]; } small_buffer; } impl; 71 / 143
  • 72. Одна оптимизация для string std::map<string, int> m; m.insert( std::pair<string, int>("Hello!", 1) ); // 1 memmove 72 / 143
  • 73. Разработка без оглядки на готовые решения
  • 74. std::variant std::variant<T...> - класс который хранит один из типов T, и помнит тип данных: union { T0 v0; T1 v1; T2 v2; // ... }; int t_index; 74 / 143
  • 75. Кто видит ошибку? template <class... T> class variant { // ... std::tuple<T...> data; }; 75 / 143
  • 76. Кто видит ошибку? variant<T...>& operator=(const U& value) { if (&value == this) { return *this; } destroy(); // data->~Ti() construct_value(value); // new (data) Tj(value); t_index = get_index_from_type<U>(); // t_index = j; return *this; } 76 / 143
  • 77. Кто видит ошибку? variant<T...>& operator=(const U& value) { if (&value == this) { return *this; } destroy(); // data->~Ti() construct_value(value); // throw; ... // ... // ~variant() { // data->~Ti() // Oops!!! 77 / 143
  • 79. У процессора есть не только кеш данных Большинство современных микропроцессоров для компьютеров и серверов имеют как минимум три независимых кэша: кэш инструкций для ускорения загрузки машинного кода, кэш данных для ускорения чтения и записи данных и буфер ассоциативной трансляции (TLB) для ускорения трансляции виртуальных (логических) адресов в физические, как для инструкций, так и для данных. [1] [1] https://ru.wikipedia.org/wiki/Кэш_процессора 79 / 143
  • 80. Кеш инструкций здоровой программы Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? 80 / 143
  • 81. Кеш инструкций с FORCE_INLINE Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totFORCE_INLINE rem aperiFORCE_INLINE eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsFORCE_INLINE voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquFORCE_INLINE est, qui dolorem ipsum, quia dolor sit, FORCE_INLINEet, consectetur, adipisci velit, sed quia non numquFORCE_INLINE eius modi tempora incidunt, ut labore et dolore magnFORCE_INLINE aliquFORCE_INLINE quaerat voluptatem. Ut enim ad minima veniFORCE_INLINE, quis nostrum exercitationem ullFORCE_INLINE corporis suscipit laboriosFORCE_INLINE, nisi ut aliquid ex ea commodi consequatur? 81 / 143
  • 82. Много бенчмарков игнорируют 2 кеша из 3х Бенчмарк должен: в бинарном виде занимать столько же, сколько ваш production код иметь приблизительно такое же количество функций и переходов, как и ваш production код 82 / 143
  • 83. True story Убрав часть шаблонных параметров из B-Tree и ощутимо уменьшив размер бинарника, получили двукратный прирост скорости. 83 / 143
  • 84. Мнение эксперта Understanding Compiler Optimization - Chandler Carrut: ”To get an interesting example <on inlining that hurts performance> it has to be big <...>”[1] "Tuning C++: Benchmarks, and CPUs, and Compilers! Oh My! - Chandler Carrut: “Primary reason why modern processors are slow is due to cache misses. <...> One of those caches is actually a cache that feeds your program to the CPU. <...> When you skip over instructions, you may skip over the cache line <...> and you stall” [2] [1] https://www.youtube.com/watch?v=FnGCDLhaxKU [2] https://www.youtube.com/watch?v=nXaxk27zwlk 84 / 143
  • 86. Aliasing Можно встретить рекомендации: использовать -fno-strict-aliasing чтобы получать меньше проблем. 86 / 143
  • 87. Aliasing bar qwe(foo& f, const bar& b) { f += b; return b * b; } 87 / 143
  • 88. Aliasing bar qwe(foo& f, const bar& b) { // load b // load f f += b; // load b return b * b; } 88 / 143
  • 89. Aliasing bar qwe(foo& f, const bar& b) { // load b // load f f += b; // &f == &b ??? // load b return b * b; } 89 / 143
  • 90. Aliasing 5% - 30% Speedup http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1124&context=ecetr https://habrahabr.ru/post/114117/ 90 / 143
  • 92. vector auto foo() { std::vector< std::shared_ptr<int> > res; for (unsigned i = 0; i < 1000; ++i) res.push_back( std::shared_ptr<int>(new int) ); return res; } 92 / 143
  • 93. vector auto foo() { std::vector< std::shared_ptr<int> > res; res.reserve(1000); // <====== for (unsigned i = 0; i < 1000; ++i) res.push_back( std::shared_ptr<int>(new int) ); return res; } 93 / 143
  • 94. make_shared auto foo() { std::vector< std::shared_ptr<int> > res; res.reserve(1000); for (unsigned i = 0; i < 1000; ++i) res.push_back( std::make_shared<int>() // <====== ); return res; } 94 / 143
  • 95. for (auto v: data) auto foo() { std::vector<std::string> data = get_data(); // ... for (auto v: data) res.insert( v ); return res; } 95 / 143
  • 96. for (auto v: data) auto foo() { std::vector<std::string> data = get_data(); // ... for (auto v: data) res.insert( std::move(v) // <====== ); return res; } 96 / 143
  • 97. for (auto&& v: data) auto foo() { std::vector<std::string> data = get_data(); // ... for (auto&& v: data) // <====== res.insert( std::move(v) ); return res; } 97 / 143
  • 98. vector auto foo() { std::vector<std::string> data = get_data(); // ... std::string res = data.back(); data.pop_back(); // ... return res; } 98 / 143
  • 99. vector auto foo() { std::vector<std::string> data = get_data(); // ... std::string res = std::move(data.back()); // <====== data.pop_back(); // ... return res; } 99 / 143
  • 100. Global constants #include <vector> #include <string> static const std::vector<std::string> CONSTANTS = { "Hello", "Word", "!" }; 100 / 143
  • 101. Global constants #include <string_view> static const std::string_view CONSTANTS[] = { "Hello", "Word", "!" }; 101 / 143
  • 102. Global constants #include <string_view> constexpr std::string_view CONSTANTS[] = { "Hello", "Word", "!" }; 102 / 143
  • 103. Inheritance // base.hpp struct base { // ... virtual void act() = 0; virtual ~base(){} }; 103 / 143
  • 104. Inheritance // something.cpp struct some_implementation: base { // ... void act() { /* ... */ } }; 104 / 143
  • 105. Inheritance // something.cpp struct some_implementation: base { // ... void act() override { /* ... */ } // <====== }; 105 / 143
  • 106. Inheritance // something.cpp struct some_implementation final: base { // <====== // ... void act() override { /* ... */ } }; 106 / 143
  • 107. Inheritance // something.cpp namespace { // <====== struct some_implementation final: base { // ... void act() override { /* ... */ } }; } // anonymous namespace 107 / 143
  • 108. Move constructors class my_data_struct { std::vector<int> data_; public: // ... my_data_struct(my_data_struct&& other) : data_(other.data_) {} }; 108 / 143
  • 109. Move constructors class my_data_struct { std::vector<int> data_; public: // ... my_data_struct(my_data_struct&& other) : data_(std::move(other.data_)) // <====== {} }; 109 / 143
  • 110. Move constructors class my_data_struct { std::vector<int> data_; public: // ... my_data_struct(my_data_struct&& other) noexcept // <====== : data_(std::move(other.data_)) {} }; 110 / 143
  • 111. Move constructors class my_data_struct { std::vector<int> data_; public: // ... my_data_struct(my_data_struct&& ) = default; // <====== }; 111 / 143
  • 113. Caching divs // .h extern const double fractions[256]; // .cpp const double fractions[256] = { 1.0 / i, ... }; 113 / 143
  • 114. Caching divs 2 cycles - MOV r32/64,m => 5 cycles - L1 Data Cache Latency for complex address calcs (p[n]) 12 cycles - L2 Cache Latency => 22-29 cycles - DIV r32 36-43 cycles - L3 Cache Latency 114 / 143
  • 115. Caching divs 5 cycles vs 22-29 cycles 115 / 143
  • 116. Caching divs 5 cycles vs 22-29 cycles x2-x4 faster 116 / 143
  • 117. Caching divs 5 cycles vs 22-29 cycles x2-x4 faster WOW!!! 117 / 143
  • 118. Caching divs 5 cycles vs 22-29 cycles x2-x4 faster WOW!!! Чё правда? 118 / 143
  • 119. Caching divs (недостатки) Оптимизатор не видит значения 119 / 143
  • 120. Caching divs (недостатки) Оптимизатор не видит значения: нет возможности оптимизировать ближайшие инструкции 120 / 143
  • 121. Caching divs (недостатки) Оптимизатор не видит значения: нет возможности оптимизировать ближайшие инструкции нет возможности constexpr 121 / 143
  • 122. Caching divs (недостатки) Оптимизатор не видит значения: нет возможности оптимизировать ближайшие инструкции нет возможности constexpr aliasing (ссылка на double может указывать на ту же ячейку памяти) 122 / 143
  • 123. Caching divs (недостатки) Серьёзное негативное влияние на производительность: нет возможности оптимизировать ближайшие инструкции нет возможности constexpr aliasing (ссылка на double может указывать на ту же ячейку памяти) 123 / 143
  • 124. Caching divs (недостатки) Серьёзное негативное влияние на производительность: нет возможности оптимизировать ближайшие инструкции нет возможности constexpr aliasing (ссылка на double может указывать на ту же ячейку памяти) Мы потратили кучу времени на обсуждение этого... 124 / 143
  • 125. Caching divs (недостатки) Серьёзное негативное влияние на производительность: нет возможности оптимизировать ближайшие инструкции нет возможности constexpr aliasing (ссылка на double может указывать на ту же ячейку памяти) Мы потратили кучу времени на обсуждение этого... Потратили 1Kb L1 кеша из 16/32Kb 111 / 143
  • 126. Caching divs (недостатки) Потратили 1Kb L1 кеша из 16/32Kb "Зачастую, производительность системы ограничена не скоростью процессора, а производительностью подсистем памяти. В то время, как традиционно компиляторы уделяли большее внимание оптимизации работы процессора, сегодня больший упор следует делать на более эффективное использование иерархии памяти." [1] [1] Ахо, Сети, Ульман. Компиляторы. Принципы, технологии, инструменты. 2ed.2008 126 / 143
  • 127. Caching divs (недостатки) Потратили 1Kb L1 кеша из 16/32Kb x10 прирост производительности без изменения инструкций [1] => Кеш очень важен [1] Ulrich Drepper. What Every Programmer Should Know About Memory. 2007 127 / 143
  • 134. РГ21 134 / 143 https://stdcpp.ru/proposals Поможем с формальностями Поможем с принятием в Boost Поможем с написанием proposal Защитим ваши интересы на заседнии, передадим отзывы
  • 135. РГ21. Идеи в разработке: 135 / 143
  • 136. РГ21. Идеи в разработке: 136 / 143 P0539R0: wide_int by Игорь Клеванец
  • 137. РГ21. Идеи в разработке: 137 / 143 P0539R0: wide_int by Игорь Клеванец P0275R1: Dynamic library load
  • 138. РГ21. Идеи в разработке: 138 / 143 P0539R0: wide_int by Игорь Клеванец P0275R1: Dynamic library load ConcurrentHashMap by Сергей Мурылев, Антон Малахов
  • 139. РГ21. Идеи в разработке: 139 / 143 P0539R0: wide_int by Игорь Клеванец P0275R1: Dynamic library load ConcurrentHashMap by Сергей Мурылев, Антон Малахов Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, …
  • 140. РГ21. Идеи в разработке: 140 / 143 P0539R0: wide_int by Игорь Клеванец P0275R1: Dynamic library load ConcurrentHashMap by Сергей Мурылев, Антон Малахов Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, … ?dlsym?
  • 141. РГ21. Идеи в разработке: 141 / 143 P0539R0: wide_int by Игорь Клеванец P0275R1: Dynamic library load ConcurrentHashMap by Сергей Мурылев, Антон Малахов Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, … ?dlsym? Stacktrace
  • 142. РГ21. Идеи в разработке: 142 / 143 P0539R0: wide_int by Игорь Клеванец P0275R1: Dynamic library load ConcurrentHashMap by Сергей Мурылев, Антон Малахов Constexpr: “D0202R2: Constexpr algorithm”, std::array, <numeric>, … ?dlsym? Stacktrace 100500 мелочей, которые делают все в ISO WG21