Я экспериментирую с расширениями tsx в haswell, адаптируя существующую кодовую базу среднего размера (1000 строк) к использованию транзакционных расширений памяти GCC (которые косвенно используют haswell tsx в этой машине) вместо грубозернистых блокировок. Я использую расширения GCC для транзакции, не пишу свой собственный _xbegin / _xend напрямую. Я использую ITM_DEFAULT_METHOD = htm
У меня проблемы с тем, чтобы заставить его работать достаточно быстро, потому что по загадочным причинам я получаю высокую скорость прерывания аппаратных транзакций. Как показано ниже, эти прерывания не связаны ни с конфликтами, ни с ограничениями пропускной способности.
Вот команда perf, которую я использовал для количественной оценки частоты отказов и основных причин:
perf stat \
-e cpu/event=0x54,umask=0x2,name=tx_mem_abort_capacity_write/ \
-e cpu/event=0x54,umask=0x1,name=tx_mem_abort_conflict/ \
-e cpu/event=0x5d,umask=0x1,name=tx_exec_misc1/ \
-e cpu/event=0x5d,umask=0x2,name=tx_exec_misc2/ \
-e cpu/event=0x5d,umask=0x4,name=tx_exec_misc3/ \
-e cpu/event=0x5d,umask=0x8,name=tx_exec_misc4/ \
-e cpu/event=0x5d,umask=0x10,name=tx_exec_misc5/ \
-e cpu/event=0xc9,umask=0x1,name=rtm_retired_start/ \
-e cpu/event=0xc9,umask=0x2,name=rtm_retired_commit/ \
-e cpu/event=0xc9,umask=0x4,name=rtm_retired_aborted/pp \
-e cpu/event=0xc9,umask=0x8,name=rtm_retired_aborted_misc1/ \
-e cpu/event=0xc9,umask=0x10,name=rtm_retired_aborted_misc2/ \
-e cpu/event=0xc9,umask=0x20,name=rtm_retired_aborted_misc3/ \
-e cpu/event=0xc9,umask=0x40,name=rtm_retired_aborted_misc4/ \
-e cpu/event=0xc9,umask=0x80,name=rtm_retired_aborted_misc5/ \
./myprogram -th 1 -reps 3000000
Итак, программа запускает некоторый код с транзакциями в нем 30 миллионов раз. Каждый запрос включает в себя одну транзакцию GCC __transaction_atomic
блок. В этом прогоне есть только одна нить.
Этот конкретный perf
Команда захватывает большинство соответствующих событий производительности TSX, описанных в Руководство по разработке программного обеспечения Intel, том 3.
Выход из perf stat
является следующим:
0 tx_mem_abort_capacity_write [26.66%]
0 tx_mem_abort_conflict [26.65%]
29,937,894 tx_exec_misc1 [26.71%]
0 tx_exec_misc2 [26.74%]
0 tx_exec_misc3 [26.80%]
0 tx_exec_misc4 [26.92%]
0 tx_exec_misc5 [26.83%]
29,906,632 rtm_retired_start [26.79%]
0 rtm_retired_commit [26.70%]
29,985,423 rtm_retired_aborted [26.66%]
0 rtm_retired_aborted_misc1 [26.75%]
0 rtm_retired_aborted_misc2 [26.73%]
29,927,923 rtm_retired_aborted_misc3 [26.71%]
0 rtm_retired_aborted_misc4 [26.69%]
176 rtm_retired_aborted_misc5 [26.67%]
10.583607595 seconds time elapsed
Как видно из вывода:
rtm_retired_start
количество составляет 30 миллионов (соответствует вводу в программу)rtm_retired_abort
счетчик примерно одинаков (не фиксирует вообще)abort_conflict
а также abort_capacity
количество равно 0, так что это не причины. Кроме того, напомним, что работает только один поток, конфликты должны быть редкими.tx_exec_misc1
а также rtm_retired_aborted_misc3
, которые несколько похожи в описании.Руководство Intel (том 3) определяет rtm_retired_aborted_misc3
счетчики:
код: C9H 20H
мнемоника: RTM_RETIRED.ABORTED_MISC3
description: Количество раз, когда выполнение RTM прерывалось из-за недружественных инструкций HLE.
Определение для tx_exec_misc1
имеет несколько похожих слов:
код: 5DH 01H
мнемоника: TX_EXEC.MISC1
description: подсчитывает, сколько раз выполнялся класс инструкций, которые могут вызвать прерывание транзакции. Так как это счетчик выполнения, он не всегда может вызвать прерывание транзакции.
Я проверил место сборки для прерываний, используя perf record / perf report, используя поддержку высокой точности (PEBS) для rtm_retired_aborted
, Местоположение имеет mov
инструкция из реестра, чтобы зарегистрироваться. Никаких странных названий инструкций поблизости не видно.
Обновить:
Вот две вещи, которые я пробовал с тех пор:
1) подпись tx_exec_misc1 и rtm_retired_aborted_misc3, которую мы видим здесь, может быть получена, например, фиктивным блоком вида
for (int i = 0; i < 10000000; i++){
__transaction_atomic{
_xabort(1);
}
}
или один из формы
for (int i = 0; i < 10000000; i++){
__transaction_atomic{
printf("hello");
fflush(stdout);
}
}
В обоих случаях счетчики перфорации выглядят похоже на то, что я вижу. Однако в обоих случаях perf report
за -e cpu/tx-abort/
указывает на интуитивно правильные линии сборки: xabort
инструкция для первого примера и syscall
один для второго. В реальной кодовой базе отчет perf указывает на толчок стека в самом начале функции:
: 00000000004167e0 <myns::myfun()>:
100.00 : 4167e0: push %rbp
0.00 : 4167e1: mov %rsp,%rbp
0.00 : 4167e4: push %r15
Я также запускал ту же команду под эмулятором разработки программного обеспечения Intel. Оказывается, проблема в этом случае исчезнет: я не получаю прерывания в том, что касается приложения.
Некоторое время это было так, но я нашел этот вопрос без ответа при поиске, поэтому вот ответ: Это аппаратная ошибка в Haswell и ранних чипах Broadwell.
Конкретная аппаратная ошибка, назначенная Intel, HSW136, и не может быть исправлено с помощью обновлений микрокода. Действительно, я думаю, что в шаге 4 эта функция больше не сообщалась как доступная cpuid
инструкция, даже когда на чипе был (неисправен) кремний для его реализации.