Я тестировал функцию и вижу, что некоторые итерации выполняются медленнее, чем другие.
После некоторых тестов я попытался сравнить два смежных измерения, и у меня все еще были странные результаты.
код на wandbox .
Для меня важная часть:
using clock = std::chrono::steady_clock;
// ...
for (int i = 0; i < statSize; i++)
{
auto t1 = clock::now();
auto t2 = clock::now();
}
Цикл оптимизирован вне , как мы видим на godbolt .
call std::chrono::_V2::steady_clock::now()
mov r12, rax
call std::chrono::_V2::steady_clock::now()
Код был скомпилирован с:
g++ bench.cpp -Wall -Wextra -std=c++11 -O3
и gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
на процессоре Intel® Xeon® W-2195 .
Я был единственным пользователем на машине, и я стараюсь работать с и без наивысшего приоритета (nice
или chrt
), и результат был одинаковым.
РезультатЯ получил с 100 000 000 итераций было:
Ось Y в наносекундах , это результат линии
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count()
Эти 4 строки заставляют меня задуматься: нет пропусков кэша / L1 / L2 / L3 (даже если строка «L3 cache misses» кажется слишком близкой к линии L2)
Я не уверен, почему будут пропуски в кеше, возможно, это результат хранения, но его нет в измеряемом коде.
Я попытался запустить программу 10 000 раз сцикл 1500, потому что кэш L1 этого процессора:
lscpu | grep L1
L1d cache: 32K
L1i cache: 32K
и 1500*16 bits = 24 000 bits
, что меньше 32 КБ, поэтому не должно быть пропуска кеша.
И результаты:
У меня все еще есть 4 строки (и немного шума).
Так что, если это действительно промах кеша, я неЯ имею представление, почему это происходит.
Я не знаю, полезно ли это для вас, но я запускаю:
sudo perf stat -e cache-misses,L1-dcache-load-misses,L1-dcache-load ./a.out 1000
Со значением 1 000 / 10 000 / 100 000 / 1 000 000
Iполучил от 4,70 до 4,30% всех обращений к L1-dcache, что мне кажется довольно приличным.
Итак, вопросы:
- В чем причинаиз этих замедлений?
- Как произвести качественный тест функции, когда у меня нет постоянного времени для операции «Нет»?
PS: я неКвон, если яотсутствует полезная информация / флаги, не стесняйтесь спрашивать!
Как воспроизвести :
Код:
#include <iostream>
#include <chrono>
#include <vector>
int main(int argc, char **argv)
{
int statSize = 1000;
using clock = std::chrono::steady_clock;
if (argc == 2)
{
statSize = std::atoi(argv[1]);
}
std::vector<uint16_t> temps;
temps.reserve(statSize);
for (int i = 0; i < statSize; i++)
{
auto t1 = clock::now();
auto t2 = clock::now();
temps.push_back(
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
}
for (auto t : temps)
std::cout << (int)t << std::endl;
return (0);
}
Сборка:
g++ bench.cpp -Wall -Wextra -std=c++11 -O3
Генерировать вывод (необходимо sudo):
В этом случае я запускаю программу 10 000 раз.Каждый раз беру 100 тактов, и я удаляю первое, которое всегда примерно в 5 раз медленнее:
for i in {1..10000} ; do sudo nice -n -17 ./a.out 100 | tail -n 99 >> fast_1_000_000_uint16_100 ; done
Создание графика:
cat fast_1_000_000_uint16_100 | gnuplot -p -e "plot '<cat'"
Результат, который я имею на моей машине:
Где я нахожусь после ответа Зулан и все комментарии
current_clocksource
установлен на tsc
, а переключатель dmesg
не виден, используемая команда:
dmesg -T | grep tsc
I useэтот сценарий для удаления HyperThreading (HT), затем
grep -c proc /proc/cpuinfo
=> 18
Вычтите 1 из последнего результата, чтобы получить последнее доступное ядро:
=> 17
Редактировать / etc / grub /по умолчанию и добавить isolcpus = (последний результат) в GRUB_CMDLINE_LINUX:
GRUB_CMDLINE_LINUX="isolcpus=17"
Окончательно:
sudo update-grub
reboot
// reexecute the script
Теперь я могу использовать:
taskset -c 17 ./a.out XXXX
Итак, я запускаю10 000 раз цикл из 100 итераций.
for i in {1..10000} ; do sudo /usr/bin/time -v taskset -c 17 ./a.out 100 > ./core17/run_$i 2>&1 ; done
Проверьте, есть ли Involuntary context switches
:
grep -L "Involuntary context switches: 0" result/* | wc -l
=> 0
Нет, хорошо.Давайте построим график:
for i in {1..10000} ; do cat ./core17/run_$i | head -n 99 >> ./no_switch_taskset ; done
cat no_switch_taskset | gnuplot -p -e "plot '<cat'"
Результат:
Есть еще 22 меры, превышающие 1000 (при большинстве значений около 20) что я не понимаю.
Следующий шаг, TBD
Выполните часть:
sudo nice -n -17 perf record...
из ответа Зулана