1 of 25

Типичная разработка на С++

или

Как я диагностировал множественные прострелы ног

Василий Шестак, Imagine Software

vass@ciklum.com

2 of 25

Распределение знаний типичного программиста на С++

«Типичная разработка на С++: вы решаете проблемы, которые создает ваш же инструмент.»� Anonymous

«При помощи C вы легко можете выстрелить себе в ногу. При помощи C++ это сделать сложнее, но если это произойдёт, вам оторвёт всю ногу целиком.» � Bjarne Stroustrup

2

3 of 25

Не мешайте fork() и потоки

3

4 of 25

Не мешайте fork() и потоки

4

5 of 25

Не мешайте fork() и потоки

5

  1. pthread_atfork
  2. Отказ от fork()
  3. Перегрузка небезопасных функций

6 of 25

Перегрузка небезопасных функций

6

/**

* Guards the given fork-unsafe function to make forking safer.

* If itsAboutToFork flag set, function stops all the threads which try to execute fork-unsafe function

*/

template <typename… Ts, typename F>

auto invokeForkUnsafeFunction(F* func, Ts… args) noexcept

{

UnsafeFunctionTracker guard;

return func(args…);

}

/**

* Shadows the localtime_r glibc function to make forking safer.

* If itsAboutToFork flag set, function stops all the threads which try to execute localtime_r.

*/

tm* localtime_r(const time_t* clock, tm* res) noexcept

{

using ltr_t = tm* (*)(const time_t*, tm*);� static auto sys_localtime_r = (ltr_t)dlsym(RTLD_NEXT, “localtime_r”);

return invokeForkUnsafeFunction(sys_localtime_r, clock, res);

}

7 of 25

Не мешайте fork() и C++

7

const auto pid = fork();

if (pid == 0)

{

logger().reinit();

logger(log::Info) << “Process started”;

int status = doSomething();

exit(status);

}

8 of 25

Советы

8

  1. Не мешайте fork() и потоки
  2. Откажитесь от fork()*
  3. Всегда помните о возможных C++ исключениях

* Исключение - fork() + exec()

9 of 25

Как GCC способен заблудиться в двух сегментах

9

10 of 25

Python GDB[1]

10

Преимущества:

  • Не нужно пересобирать приложение.
  • Внесение изменений “на лету”.
  • Нет необходимости модифицировать исходники.
  • Контекст отладки.

Недостатки:

  • Влияет на скорость работы приложения.

[1]Extending GDB using Python

11 of 25

Python GDB

11

def print_var(names):

for name in names:

print name + ‘=’

gdb.execute(‘print’ + name)

gdb.execute(‘info address’ + name)

l = []

l.append(Tracepoint(‘ComplexObject::ComplexObject’, lambda: print_var([‘this’])))

l.append(Tracepoint(‘ComplexObject::~ComplexObject’, lambda: print_var([‘this’])))

l.append(Tracepoint(ComplexObject::~ComplexObject’, lambda: print_backtrace))

12 of 25

static и thread_local

mov rax,QWORD PTR ds:0x50� movzx eax,BYTE PTR [rax]� test al,al #check a guard- jne 0x6e13665�|�|�| mov rdi,QWORD PTR ds:0x58�| call 0x6e138f4<Flag::Flag()> #init�|�|�| mov rax,QWORD PTR ds:0x50�| mov BYTE PTR [rax],0x1 #set a guard|�->mov rdi,QWORD PTR ds:0x58� call 0x6e164f4<Flag::get()> #use it

mov rax,QWORD PTR fs:0x0� lea rax,[rax-0x28]� movzx eax,BYTE PTR [rax]� test al,al #check a guard- jne 0x6e13685�| mov rax,QWORD PTR fs:0x0�| lea rax,[rax-0x18]�| mov rdi,rax�| call 0x6e138f4<Flag::Flag()> #init�| mov rax,QWORD PTR fs:0x0�| lea rax,[rax-0x28]�| mov BYTE PTR [rax],0x1 #set a guard�->mov rax,QWORD PTR fs:0x0� lea rax,[rax-0x18]� mov rdi,rax� call 0x6e164f4<Flag::get()> #use it

mov rax,QWORD PTR fs:0x0� mov rax,QWORD PTR ds:0x0� lea rax,[rax-0x28]� movzx eax,BYTE PTR [rax]� test al,al #check a guard- jne 0x6e13685�| mov rax,QWORD PTR fs:0x0�| lea rax,[rax-0x18]�| mov rdi,rax�| call 0x6e138f4<Flag::Flag()> #init�| mov rax,QWORD PTR fs:0x0�| lea rax,[rax-0x28]�| mov BYTE PTR [rax],0x1 #set a guard�->mov rax,QWORD PTR fs:0x0� lea rax,[rax-0x18]� mov rdi,rax� call 0x6e164f4<Flag::get()> #use it

12

13 of 25

Советы

13

  1. В любой непонятной ситуации смотрите дизассемблерный листинг.
  2. Знайте свой компилятор.
  3. Регулярно обновляйте версию компилятора.

14 of 25

Знай свой компилятор

14

15 of 25

Знай свой компилятор[1]

15

// Деструктор вызван не будет

16 of 25

Наивный lock-free

16

17 of 25

Наивный lock-free

17

18 of 25

Memory reordering

18

mov [_x], 1

mov ax, [_y]

mov ax, [_y]

mov [_x], 1

Thread 0

Thread 1

store(zeroWants, 1)

store(oneWants, 1)

store(victim, 0)

store(victim, 1)

r0 = load(oneWants)

r0 = load(zeroWants)

r1 = load(victim)

r1 = load(victim)

Thread 0

Thread 1

r0 = load(oneWants)

r0 = load(zeroWants)

store(zeroWants, 1)

store(oneWants, 1)

store(victim, 0)

store(victim, 1)

r1 = load(victim)

r1 = load(victim)

19 of 25

Наивный lock-free

  1. volatile == memory fence[1]?
  2. Используйте инструменты C++ (std::atomic_thread_fence, std::atomic)
  3. Используйте инструменты специфичные для ОС/архитектуры

[1] Microsoft specific volatile behavior

19

20 of 25

Советы

  1. Используя volatile помни, что он спасает только от оптимизаций компилятора.
  2. Оптимизации процессора разнятся в зависимости от архитектуры.
  3. RTFM!

20

21 of 25

Как “ускорить” double?

21

22 of 25

Борьба с ошибками округления

  1. Оперировать с числами одного “ранга”.
  2. Использовать специализированные библиотеки[1].
  3. Использовать статический анализатор[2].
  4. Символьные вычисления.
  5. Алгоритм Кэхэна��[1] GMP�[2] frama-c

22

23 of 25

Ускорение расчетов

  1. GMP
  2. -ffast-math (SSE/AVX, трансформации)
  3. -funsafe-math-optimizations
  4. CUDA/OpenCL

��

23

24 of 25

Вопросы?

25 of 25

Благодарю�за внимание!