Стоимость исключений C ++ и setjmp / longjmp

Я написал тест, чтобы измерить стоимость исключений C ++ с потоками.

#include <cstdlib>
#include <iostream>
#include <vector>
#include <thread>

static const int N = 100000;

static void doSomething(int& n)
{
--n;
throw 1;
}

static void throwManyManyTimes()
{
int n = N;
while (n)
{
try
{
doSomething(n);
}
catch (int n)
{
switch (n)
{
case 1:
continue;
default:
std::cout << "error" << std::endl;
std::exit(EXIT_FAILURE);
}
}
}
}

int main(void)
{
int nCPUs = std::thread::hardware_concurrency();
std::vector<std::thread> threads(nCPUs);
for (int i = 0; i < nCPUs; ++i)
{
threads[i] = std::thread(throwManyManyTimes);
}
for (int i = 0; i < nCPUs; ++i)
{
threads[i].join();
}
return EXIT_SUCCESS;
}

Вот версия C, которую я изначально написал для удовольствия.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <glib.h>

#define N 100000

static GPrivate jumpBuffer;

static void doSomething(volatile int *pn)
{
jmp_buf *pjb = g_private_get(&jumpBuffer);

--*pn;
longjmp(*pjb, 1);
}

static void *throwManyManyTimes(void *p)
{
jmp_buf jb;
volatile int n = N;

(void)p;
g_private_set(&jumpBuffer, &jb);
while (n)
{
switch (setjmp(jb))
{
case 0:
doSomething(&n);
case 1:
continue;
default:
printf("error\n");
exit(EXIT_FAILURE);
}
}
return NULL;
}

int main(void)
{
int nCPUs = g_get_num_processors();
GThread *threads[nCPUs];
int i;

for (i = 0; i < nCPUs; ++i)
{
threads[i] = g_thread_new(NULL, throwManyManyTimes, NULL);
}
for (i = 0; i < nCPUs; ++i)
{
g_thread_join(threads[i]);
}
return EXIT_SUCCESS;
}

Версия C ++ работает очень медленно по сравнению с версией C.

$ g++ -O3 -g -std=c++11 test.cpp -o cpp-test -pthread
$ gcc -O3 -g -std=c89 test.c -o c-test `pkg-config glib-2.0 --cflags --libs`
$ time ./cpp-test

real    0m1.089s
user    0m2.345s
sys     0m1.637s
$ time ./c-test

real    0m0.024s
user    0m0.067s
sys     0m0.000s

Итак, я запустил профилировщик callgrind

За cpp-test, __cxz_throw был вызван ровно 400 000 раз с себестоимостью 8 000 032.

За c-test, __longjmp_chk был вызван ровно 400 000 раз с себестоимостью 5 600 000.

Вся стоимость cpp-test 4,048,441,756.

Вся стоимость c-test 60,417,722.


Я предполагаю что-то гораздо большее, чем просто сохранение состояния точки перехода и последующее возобновление выполняется с исключениями C ++. Я не мог проверить с большим N потому что профилировщик callgrind будет работать вечно для теста C ++.

Какая дополнительная стоимость, связанная с исключениями в C ++, делает ее во много раз медленнее, чем setjmp/longjmp пара хотя бы в этом примере?

16

Решение

Это по замыслу.

Ожидается, что исключения C ++ будут исключительный в природе и оптимизированы таким образом. Программа скомпилирована, чтобы быть наиболее эффективной, когда исключение не происходит.

Вы можете убедиться в этом, закомментировав исключение из ваших тестов.

В C ++:

    //throw 1;

$ g++ -O3 -g -std=c++11 test.cpp -o cpp-test -pthread

$ time ./cpp-test

real    0m0.003s
user    0m0.004s
sys     0m0.000s

В С:

    /*longjmp(*pjb, 1);*/

$ gcc -O3 -g -std=c89 test.c -o c-test `pkg-config glib-2.0 --cflags --libs`

$ time ./c-test

real    0m0.008s
user    0m0.012s
sys     0m0.004s

Какая дополнительная стоимость, связанная с исключениями в C ++, делает ее во много раз медленнее, чем пара setjmp / longjmp, по крайней мере, в этом примере?

g ++ реализует исключения модели с нулевой стоимостью, которые не имеют эффективных издержек *, когда исключение не брошен. Машинный код выдается как будто не было try/catch блок.

Стоимость этого нулевые накладные расходы является то, что поиск таблицы должен быть выполнен на счетчике программы, когда исключение брошен, определить переход к соответствующему коду для выполнения раскрутки стека. Это ставит весь try/catch реализация блока в коде, выполняющем throw,

Ваша дополнительная стоимость — поиск по таблице.

* Может произойти незначительное временное вуду, так как наличие таблицы поиска на ПК может повлиять на структуру памяти, что может повлиять на ошибки кэша ЦП.

18

Другие решения


По вопросам рекламы [email protected]