Программа тестирования для выявления неисправности ЦП

Я написал многопоточную программу, чтобы продемонстрировать эффект выхода процессора Intel из строя. Программа прикреплена в конце поста. Ожидаемый результат должен заключаться в том, что когда x будет распечатан как 42 или 0 обработчиком1. Однако фактический результат всегда равен 42, а это означает, что эффекта нарушения порядка не происходит.

Я скомпилировал программу с помощью команды «gcc -pthread -O0 out-of-order-test.c». Я запускаю скомпилированную программу на Ubuntu 12.04 LTS (ядро Linux 3.8.0-29-generic) на процессоре Intel IvyBridge Intel (R ) Процессор Xeon (R) E5-1650 v2.

Кто-нибудь знает, что мне делать, чтобы увидеть эффект выхода из строя?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int f = 0, x = 0;

void* handler1(void *data)
{
    while (f == 0);
    // Memory fence required here
    printf("%d\n", x);
}

void* handler2(void *data)
{
    x = 42;
    // Memory fence required here
    f = 1;
}

int main(int argc, char argv[])
{
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, handler1, NULL);
    pthread_create(&tid2, NULL, handler2, NULL);

    sleep(1);
    return 0;
}

person Mike    schedule 21.11.2015    source источник
comment
Это не о неисправности, а о состоянии гонки. (x86 - это упорядоченная архитектура загрузки / сохранения, кстати.).   -  person too honest for this site    schedule 22.11.2015
comment
@Olaf, спасибо за комментарий. Тем не менее, x86 по крайней мере имеет проблему загрузки после сохранения в соответствии с stackoverflow.com/questions/7346893/. Если у него есть зависимость данных от одного ядра, я знаю, что оборудование будет поддерживать порядок. В противном случае механизм нарушения порядка может выполнить следующую инструкцию перед предыдущей.   -  person Mike    schedule 22.11.2015
comment
out of order здесь, в любом случае, не об исполнении инструкции. Едва ли возможна архитектура с множеством задач, такая как (почти) все высокопроизводительные архитектуры. Речь идет о загрузке / хранении. Но вы не будете здесь ничего эксплуатировать со своим ко. И, вероятно, не с каким-либо кодом C - по крайней мере, не надежно. Последнее просто потому, что вам нужна конкретная последовательность инструкций, которую вы не сможете контролировать при использовании компилятора. Итак, погрузитесь в ассемблер и попробуйте. Удачи.   -  person too honest for this site    schedule 22.11.2015
comment
Вы ищете preshing.com/20120515/memory-reordering-caught в действии. Код Джеффа Прешинга продемонстрирует переупорядочение StoreLoad на x86 (единственный вариант, который возможен на x86). Вы никогда не увидите переупорядочения StoreStore на x86, потому что он строго упорядочен. У каждого магазина есть семантика выпуска, и у каждой загрузки есть семантика приобретения. Прочтите другие сообщения в блоге Прешинга, чтобы понять, что это значит.   -  person Peter Cordes    schedule 22.11.2015
comment
В слабоупорядоченной архитектуре, такой как ARM или PPC, ваш код может наблюдатель f = 1 без x = 42, но вы должны тестировать в цикле. Маловероятно, что одно лишь тестирование что-нибудь найдет. Оба хранилища, вероятно, будут видны глобально до запуска второго потока. (И запуск потока, вероятно, в какой-то момент все равно запустит инструкцию по ограничению памяти!)   -  person Peter Cordes    schedule 22.11.2015


Ответы (2)


ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ: следующее касается только изменения порядка ПАМЯТИ. Насколько мне известно, вы не можете наблюдать неупорядоченное выполнение вне конвейера, поскольку это означало бы отказ ЦП придерживаться своего интерфейса. (например: вы должны сообщить Intel, это будет ошибкой). В частности, должна быть ошибка в буфере повторного заказа и в бухгалтерском учете списания инструкций.

Согласно документации Intel ( в частности Том 3A, раздел 8.2.3.4):

Модель упорядочивания памяти Intel-64 позволяет переупорядочить загрузку с более ранним хранением в другое место.

Он также указывает (я резюмирую, но все это доступно в разделе 8.2 Упорядочивание памяти с примерами в 8.2.3), что загрузки никогда не переупорядочиваются с загрузками, хранилища никогда не переупорядочиваются с хранилищами и хранятся и никогда не переупорядочиваются с более ранними загрузками. . Это означает, что между этими операциями в Intel 64 существуют неявные ограждения (3 слабых типа).

Чтобы наблюдать переупорядочение памяти, вам просто нужно реализовать этот пример с достаточной осторожностью, чтобы действительно наблюдать эффекты. Вот ссылка на полную мою реализацию, демонстрирующую это. (Я расскажу более подробную информацию в сопроводительном сообщении здесь ).

По сути, первый поток (processor_0 из примера) делает следующее:

    x = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r1 = y;

внутри цикла while в собственном потоке (закрепленном на ЦП с помощью SCHED_FIFO:99).

Второй (наблюдатель в моей демонстрации) делает следующее:

    y = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r2 = x;

также в while цикле в собственном потоке с теми же настройками планировщика.

Повторные заказы проверяются следующим образом (точно так, как указано в примере):

if (r1 == 0 and r2 == 0)
++reorders;

При отключенном CPU_FENCE я вижу следующее:

[  0][myles][~/projects/...](master) sudo ./build/ooo
after 100000 attempts, 754 reorders observed

При включенном CPU_FENCE (который использует "тяжелую" mfence инструкцию) я вижу:

[  0][myles][~/projects/...](master) sudo ./build/ooo
after 100000 attempts, 0 reorders observed

Надеюсь, это проясняет вам ситуацию!

person Myles Hathcock    schedule 22.11.2015
comment
Большое спасибо за ваше внимательное и полезное объяснение и особенно за код! Я запускаю его на своем компьютере, и он работает !!! Я очень ценю вашу помощь! - person Mike; 22.11.2015
comment
Рад, что ты нашел это полезным! - person Myles Hathcock; 23.11.2015

Вы смешиваете состояние гонки с парадигмой выполнения вне очереди. К сожалению, я почти уверен, что вы не можете "раскрыть" выполнение вне очереди, поскольку оно явно спроектировано и реализовано таким образом, чтобы оградить вас (работающую программу и ее данные) от ее последствий.

Более конкретно: выполнение вне очереди происходит «внутри» ЦП в полном объеме. Результаты неупорядоченных инструкций не отправляются напрямую в файл реестра, а вместо этого помещаются в очередь для сохранения порядка. Таким образом, даже если сами инструкции выполняются не по порядку (на основе различных правил, которые в первую очередь гарантируют, что эти инструкции могут выполняться независимо друг от друга), их результаты всегда переупорядочиваются, чтобы они были в правильной последовательности, как ожидает сторонний наблюдатель. .

Ваша программа делает следующее: она пытается (очень грубо) имитировать состояние гонки, в котором вы надеетесь увидеть, что назначение f будет выполнено раньше x и в то же время вы надеетесь получить переключение контекста происходит точно в тот самый момент, и вы предполагаете, что новый поток будет запланирован на том же ядре ЦП, что и другой. Однако, как я объяснил выше, даже если вам посчастливится выполнить все перечисленные условия (запланировать второй поток сразу после f назначения, но до x назначения и запланировать новый поток на то же самое ядро ​​ЦП), что само по себе является событием с чрезвычайно низкой вероятностью - даже в этом случае все, что вы действительно обнаруживаете, - это потенциальное состояние гонки, но не выполнение вне очереди.

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

Вы можете прочитать немного больше об исполнении вне очереди здесь: http://courses.cs.washington.edu/courses/csep548/06au/lectures/introOOO.pdf

ОБНОВЛЕНИЕ Поразмыслив, я думаю, вы могли бы изменить инструкции на лету в надежде выявить нарушение порядка выполнения. Но даже в этом случае я боюсь, что этот подход не удастся, поскольку новая «обновленная» инструкция не будет правильно отражена в конвейере процессора. Я имею в виду: ЦП, скорее всего, уже получил и проанализировал инструкцию, которую вы собираетесь изменить, поэтому то, что будет выполнено, больше не будет соответствовать содержимому слова памяти (даже того, которое находится в кэше L1 ЦП). Но этот метод, предполагающий, что он может вам помочь, требует некоторого продвинутого программирования непосредственно на ассемблере и требует, чтобы ваш код работал с наивысшим уровнем привилегий (кольцо 0). Я бы порекомендовал проявлять крайнюю осторожность при написании самомодифицирующегося кода, поскольку он имеет большой потенциал побочных эффектов.

person YePhIcK    schedule 21.11.2015
comment
@Yephlck, Большое спасибо за подробное объяснение и исправление! Теперь я понял вашу точку зрения: процессор будет выполнять некорректную работу внутри своего конвейера, но когда он показывает результат извне, он всегда меняет порядок, чтобы он находился в исходной последовательности. Основываясь на этой истине, я думаю, что моя программа никогда, вместо сверхмалой вероятности, никогда не обнаружит эффект нарушения порядка, потому что f всегда назначается после того, как x присваивается в handler2 (). Я прав? - person Mike; 22.11.2015