713 Тем для обсуждения
6,530 Открытых обсуждений
- Association for Computing Machinery TechNews (ACM)
- Go Parallel! (Dr. Dobbs)
- HPCwire (Tabor Communications, Inc.)
- insideHPC (John West)
- Joe Duffy's Weblog (Microsoft)
- Microsoft Parallel Programming Development Center (Microsoft Germany)
- MultiCoreInfo.com
- scalability.org (Scalable Informatics)
- Software Dev Blog (Intel Germany)
- Soft Talk Blog (Intel United Kingdom)
- The Moth (Microsoft)
Загадочная многопоточность и Parallel Inspector
Vladimir Tsymbal (Intel) (13 пост(а)) 10.07.2009 15:38
Приглашаю народ слегка поломать голову над такой вот задачкой (ибо моя уже почти сломалась
) . Берем параллелный код, написанный с помощью OpenMP, с "очевидно" заложенной туда проблемой data race, и прогоняем его в Parallel Inspector (я компилировал c Intel Compiler Pro 11.1.035)
#include "stdafx.h"
#include <omp.h>
int g_var;
void TestFunc(int par)
{
printf("%d \n", omp_get_thread_num());
if (par == 0)
g_var++;
if (par != 0)
g_var--;
}
int _tmain(int argc, _TCHAR* argv[])
{
omp_set_num_threads(2);
#pragma omp parallel for
for (int i=0; i<2; i++)
TestFunc(i);
/* the same issue */
/*
#pragma omp parallel sections
{
#pragma omp section
TestFunc(0);
#pragma omp section
TestFunc(1);
}
*/
return 0;
}
Смотрим и удивляемся - ни одной ошибки многопоточности не обнаружено!

Можно для разнообразия параллелную конструкцию попробовать заменить на #pragma omp parallel sections (закоментированная часть), но результат будет тем же.
Вывод исполнения функции omp_get_thread_num() позволяет нам судить о том, что все-таки код выполнялся в двух потоках.
Проверяем дизассемблер, на предмет оптимизации функции TestFunc(). Интеловский компилятор выдал такой код.
# if (par == 0) 0040105A mov eax,dword ptr [par] 0040105D test eax,eax 0040105F jne TestFunc+4Bh (401067h) g_var++; 00401061 inc dword ptr [g_var (4072A4h)] if (par != 0) 00401067 mov eax,dword ptr [par] 0040106A test eax,eax 0040106C je TestFunc+58h (401074h) g_var--; 0040106E dec dword ptr [g_var (4072A4h)] if (par == 0) 00411425 cmp dword ptr [par],0 00411429 jne TestFunc+58h (411438h) g_var++; 0041142B mov eax,dword ptr [g_var (417160h)] 00411430 add eax,1 00411433 mov dword ptr [g_var (417160h)],eax if (par != 0) 00411438 cmp dword ptr [par],0 0041143C je TestFunc+6Bh (41144Bh) g_var--; 0041143E mov eax,dword ptr [g_var (417160h)] 00411443 sub eax,1 00411446 mov dword ptr [g_var (417160h)],eax
То есть операции инрементации и декрементации присутствуют.
Существуют разные варианты проверки и разные направления исследования этой проблемы. Не буду их называть - оставлю уважаемому сообществу возможность подумать над этим. Того, кто разберется с ней, ждет всенародная слава и звание титана параллельного программирования
Категории: Параллельное программирование, Разработка софта
Метки: Parallel Studio
Пожалуйста, обратитесь к странице Уведомление об оптимизации для более подробной информации относительно производительности и оптимизации в программных продуктах компании Intel.
Комментарии (63)
| 10.07.2009 08:39
Vladimir Tsymbal (Intel)
| 2 Dmitriy Vyukov: Сущность таких тулов как раз и состоит в том, чтобы диагностировать ошибку, даже если она "не успела" случиться во время исполнения. Предположение о разном времени существования потоков принимается. Но позволю себе дать хинт: для исследования проблемы можно использовать и другие тулы :) |
| 10.07.2009 08:41
pdemenko
|
Слишком мало по времени работают потоки. Если один поток завершил свою работу до того, как стартовал другой, то для Инспектора они не являются параллельными. Если модифицировать функцию вот так, то датарейс находится: void TestFunc(int par) { printf("%d n", omp_get_thread_num()); if (par == 0) Sleep(2000); g_var++; if (par != 0) Sleep(2000); g_var--; } |
| 10.07.2009 11:26
Alexey Kukanov (Intel)
|
Вообще, потоки не могут завершить работу один раньше другого, потому что в конце параллельного региона OpenMP есть неявный барьер, где они синхронизируются. Я бы сказал, что это баг Inspector-а. |
| 10.07.2009 20:38
ksili
|
Может это из-за того, что функции выполняются всего по разу, и дейтвительно они очень короткие. Будет ли ошибка ловиться если функции будут вызываться большее число раз? #pragma omp parallel for for (int i=0; i<200; i++) TestFunc(i%2); |
| 11.07.2009 05:06
ilnarb
|
кажется дело просто в том что очен мало работы, не сравнимо со временем на синхронизации входа в параллельную секцию и выхода из него. а вообще, на мсте умного компилятора такие секции я бы и не параллелил а просто инлайнил прямиком))) |
| 11.07.2009 07:04
Dmitriy Vyukov
|
Слона-то мы и не заметили - почему у переменной g_var разные адреса в двух функциях: 00401061 inc dword ptr [g_var (4072A4h)] 0041142B mov eax,dword ptr [g_var (417160h)] ? |
| 11.07.2009 07:05
Dmitriy Vyukov
| Это OpenMP так постарался? |
| 11.07.2009 07:10
Dmitriy Vyukov
|
Кстати, меня мучает один вопрос по поводу Инспектора, я его задавал Eric Moore (Intel), но он так и не ответил. Если у меня есть следующий код, детектирует ли ИНспектор гонку: char buf [8]; // thread 1 InterlockedCompareExchange64((long*)buf, -1, 0); // thread 2 char r = buf[7]; ? |
| 12.07.2009 04:54
ilnarb
| Dmitriy Vyukov, а слон то может быть изза неинициализированности g_var, поправьте если я не прав |
| 12.07.2009 13:28
Vladimir Tsymbal (Intel)
|
>Слона-то мы и не заметили Нет, это я всего лишь забыл упомянуть, что часть кода, начиная со строки 14, соответствует той же функции, только скомпиоированной компилятором MSFT :) Сорри. |
| 12.07.2009 13:31
Vladimir Tsymbal (Intel)
|
>Будет ли ошибка ловиться если функции будут вызываться большее число раз? Будет. |
| 12.07.2009 19:54
ksili
| Так значит вопрос решен? Кого ждёт звание титана параллельного программирования? ;-) |
| 13.07.2009 03:23
Dmitriy Vyukov
|
Re: а слон то может быть изза неинициализированности g_var, поправьте если я не прав Глобальные переменные автоматически инициализируются нулём. |
| 13.07.2009 05:44
Vladimir Tsymbal (Intel)
|
Re: Так значит вопрос решен? Кого ждёт звание титана параллельного программирования? ;-) Я не увидел формулировки причины данной проблемы. То, что "функции выполняются всего по разу, и дейтвительно они очень короткие", является лишь логически не до конца развитым предположением и без обоснования. Кстати, похоже никто пока не пробовал использовать другие подходы и инструменты для измерения :( |
| 13.07.2009 07:44
Maxym Dmytrychenko (Intel)
|
Thread Checker показывает конфликт Write -> Read data-race Memory read at "Parallel Inspector.cpp":14 conflicts with a prior memory write at "Parallel Inspector.cpp":12 (flow dependence) "0x1072" "12" "*" " g_var++; " "0x107F" "14" "*" " g_var--; " в чем тогда вопрос :) ? |
| 13.07.2009 07:47
Vladimir Tsymbal (Intel)
|
>в чем тогда вопрос :) ? Вопрос в том, какой использовался компилятор :) Например если используется 10.0, то Thread Checker у меня ничего не показывает ;) |
| 13.07.2009 08:15
Dmitriy Vyukov
|
А какая разница какой используется компилятор? Ведь это никак не влияет на наличие гонки... Если принять, что в используемых тулзах нет ошибок. Intel в отличие от MSVC скомпилировал и инкремент и декремент в одну инстукцию, но что это меняет? |
| 13.07.2009 08:28
Vladimir Tsymbal (Intel)
|
Re: А какая разница какой используется компилятор? Ведь это никак не влияет на наличие гонки... Тут сразу два вопроса. Ответ на первый - похоже разница есть, ибо у меня TC ведет себя по-разному, в зависимости от версий компиляторов, которые я указал. (Это кстати и есть моя основная головная боль :) ) Второй вопрос - "очевидное" наличие гонки и предполагалось с самого начала :) |
| 13.07.2009 09:11
Dmitriy Vyukov
|
Re: Кстати, похоже никто пока не пробовал использовать другие подходы и инструменты для измерения :( Потому что не очень понятно, что это значит. Relacy Race Detector находит гонку, и что из этого следует? struct test : test_suite<test, 2> { var<int> g_var; test() { g_var($) = 0; } void thread(unsigned index) { if (index) g_var($)++; else g_var($)--; } }; int main() { simulate<test>(); } struct test DATA RACE (data race detected) iteration: 1 execution history: [0] 1: [CTOR BEGIN] [1] 1: <00337330> store, value=0, in test::test, main.cpp(34) [2] 1: [CTOR END] [5] 1: <00337330> load, value=0, in test::thread, main.cpp(40) [6] 1: <00337330> store, value=1, in test::thread, main.cpp(40) [7] 0: <00337330> load, value=0, in test::thread, main.cpp(42) [8] 0: DATA RACE (data race detected), in test::thread, main.cpp(42) thread 0: [7] 0: <00337330> load, value=0, in test::thread, main.cpp(42) [8] 0: DATA RACE (data race detected), in test::thread, main.cpp(42) thread 1: [0] 1: [CTOR BEGIN] [1] 1: <00337330> store, value=0, in test::test, main.cpp(34) [2] 1: [CTOR END] [5] 1: <00337330> load, value=0, in test::thread, main.cpp(40) [6] 1: <00337330> store, value=1, in test::thread, main.cpp(40) |
| 13.07.2009 09:16
Dmitriy Vyukov
|
Re: Ответ на первый - похоже разница есть, ибо у меня TC ведет себя по-разному, в зависимости от версий компиляторов, которые я указал Просто не полная/точная проверка со стороны Inspector и Checker'а. Они, конечно, предназначены для нахождения ошибок, которые "не успели" случится во время выполнения. Но эта цель в прямой конфронтации с требованием более-менее быстрой работы. Поэтому я не удивлюсь, если они инструментируют лишт некоторый процент обращений, либо инструментируют все но собирают статистику не со всех. К сожалению информации по этому поводу Интел не раскрывает насколько я знаю. Возможно там есть какие-то настройки касательно "уровня дотошности и тормознутости" проверки. |
| 13.07.2009 09:22
Vladimir Tsymbal (Intel)
|
Re: Потому что не очень понятно, что это значит. Ну я как бы намекаю народу попробовать этот же примерчик с TC. Ок, попробовали. Потом может переписать на нативном коде, и удостовериться, что Инспектор обнаруживает / не обнаруживает. И т.д. Re: Relacy Race Detector находит гонку, и что из этого следует? О, а что это за язык/симулятор такой? Я просто раньше такого не видел... Тем не менее, это не OpenMP. |
| 13.07.2009 09:24
Dmitriy Vyukov
|
Re: Второй вопрос - "очевидное" наличие гонки и предполагалось с самого начала :) А где вопрос? :) Тут гонки всё-таки нету что ли? |
| 13.07.2009 09:27
Dmitriy Vyukov
|
Re: О, а что это за язык/симулятор такой? Я просто раньше такого не видел... http://software.intel.com/en-us/blogs/2009/03/03/dont-rely-o.....-detector/ |
| 13.07.2009 09:32
Dmitriy Vyukov
|
Re: Ну я как бы намекаю народу попробовать этот же примерчик с TC. Ок, попробовали. Потом может переписать на нативном коде, и удостовериться, что Инспектор обнаруживает / не обнаруживает. И т.д. Понятно, на нас хотят спихнуть работу по тестированию Инспектора и Чекера :))) А какой из этого можно будет сделать вывод? Ну допустим такой-то тул в такой-то ситуации не обнаруживает гонку, что из этого следует кроме не полной/точной проверки? А составление полной таблицы касательно того какой тул в каких ситуациях пропускает ошибки тянет как минимум на месячную работу сотрудника Интел ;) |
| 13.07.2009 09:36
Vladimir Tsymbal (Intel)
|
Понятно, на нас хотят спихнуть работу по тестированию Инспектора и Чекера :))) Не, ну если кому-то влом попробовать разобраться с проблемой имеющимися средствами и применением знаний о параллельном программировании, яж не настаиваю... :) Тем более, что мне интересны в основном ход мыслей и рассуждения, а не результаты ;) |
| 13.07.2009 09:55
Dmitriy Vyukov
|
Re: Не, ну если кому-то влом попробовать разобраться с проблемой имеющимися средствами... А в чём тут разбираться уже не очень понятно. Факт: Инстпектор не детектирует гонку на данном примере. Если заявляется, что Инспектор должен находить все гонки со 100% вероятностью (я такого заявления не видел), значит это - ошибки в Инспекторе. Если такого не заявляется, ну значит просто не находит на данном примере и все (ряд причин, по которым это может происходить, тут предложили). |
| 13.07.2009 10:11
Vladimir Tsymbal (Intel)
| Все дело в этих самых причинах - они мне и интересны. Тут часть предложенных причин по крайней мере корректные, часть - нет (например, абсолютно противоречат принципу работы многопоточного OpenMP-приложения). |
| 13.07.2009 12:29
vlubch
|
А разве это гонки? Здесь мы имеем конфликт, связанный с одновременным доступом по записи к общей переменной двух (n) параллельных процессов. Для параллельных процессов такого вообще допускать нельзя. Или уж (если подобное допускается) как-то оговаривать такие ситуации отдельно. |
| 13.07.2009 13:04
Dmitriy Vyukov
|
А как ещё это назвать? И что тогда такое гонка, если не это? Вот определение из ISO C++: 1.10/14 The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. 1.10/3 Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location. Все остальные известные мне источники содержат аналогичные определения. |
| 13.07.2009 13:17
Dmitriy Vyukov
|
Re: Все дело в этих самых причинах - они мне и интересны. Имвхо тут надо либо посмотреть документацию (если таковая имеется) или спросить у разработчиков. Гипотезы тут можно выдвигать, но доказать скорее всего всё равно ничего не получится. Моё мнение, что Инспектор не может это поймать, т.к. конфликтующие доступы достуточно разнесены по времени. А Инспектор, учитывая, что он использует инструментирование бинарного кода, не может определить когда разнесённые по времени доступы разнесены за счёт механизма взаимного исключения, а когда просто "так получилось". В данном случае - "просто так получилось", но доказать, что это - гонка, Инспектор не может. Доказательство потребовало бы либо множество (миллионы и миллиарды) запусков программы, либо будет очень сложным и всё равно ненадёжным (выдавать либо много false positive либо много false negative). По-крайней мере я сейчас не вижу способа, как это можно делать. На это возможно накладывается тот факт, что Инспектор инструментирует и/или собирает статистику не со всех обращений к памяти (обратное может сильно тормозить выполнение). Если вот на такой программе Инспектор находит гонку, то это... ну по крайней мере не опровергает гипотезу:
void TestFunc()
{
for (size_t i = 0; i != 100000000; i += 1
((int volatile&)g_var)++;
}
|
| 15.07.2009 08:34
vlubch
| Что за дела? Отправил уже второй раз комментарий. Счетчик балов увеличился, а самого комментария нет??? |
| 15.07.2009 08:35
vlubch
|
Re: А как ещё это назвать? И что тогда такое гонка, если не это? Назвать, как я уже сказал, - ошибка общего доступа к общей переменной (одновременное изменение). Гонка - это непредсказуемый результат работы множества параллельных процессов (см. для примера http://www.citforum.ru/programming/digest/multithreading/). При гонках процессы могут совсем не общаться между собой, а конечный результат может быть разным (см. приведенную ссылку) Что мы в данном случае имеем? Два, по сути, параллельных атомарных действия. Одно – увеличивает, второе – уменьшает значение общей переменной. Имея формально равную длительность, запущенные формально в одно и то же время, они должны одновременно изменить значение общей переменной. Что в этом случае должно быть в конечном итоге? Думаю, не знает и создатель данной программы! А, кстати, какой результат действительно ожидался? А что тогда в этом случае должен «думать» TC? :) |
| 15.07.2009 08:36
vlubch
|
Re: По-крайней мере я сейчас не вижу способа, как это можно делать. Способ есть. В «три притопа»: 1) Строим модель каждого параллельного процесса 2) По параллельным моделям находим эквивалентную последовательную модель 3) Анализируем последнюю. Если некие атомарные действия параллельных процессов одновременно (в последовательной модели они окажутся на одном дискретном такте модели) пытаются изменить значения общей переменной – ахтунг! Но это шаги для выявления ошибок общего доступа. Гонка, как правило, результат некоторого случайного влияния на процесс/процессы. Если мы формализуем и эту «случайность», представив ее как еще один процесс, то мы впишемся в указанные шаги. Если нет, то не поможет и «миллионная прогонка» :) Еще одно определение гонок можно посмотреть в Wiki. См. определение состояние гонки в http://ru.wikipedia.org/wiki/онка. Жаль, что там нет определения понятия Гонки( электроника). По сути, что программные, что электронные гонки – все едино. Более того, думаю, понятие гонок в программирование пришло из электроники. С ними (гонками) там боролись задолго до появления параллельного программирования. Собственно борьбой с гонками в первую очередь интересуется теория и практика проектирования цифровых асинхронных схем. |
| 15.07.2009 08:38
vlubch
| Разбил на два - прошло! Видимо, есть ограничение на его длину. И где указано это? :) |
| 16.07.2009 03:10
Dmitry Oganezov (Intel)
|
>> разбил на два - прошло! Видимо, есть ограничение на его длину. И где указано это? :) прошу прощения, это спам-фильтр так вот неожиданно отработал. Посмотрим, что можно с этим сделать... |
| 16.07.2009 03:57
ksili
|
>прошу прощения, это спам-фильтр так вот неожиданно отработал. Посмотрим, что можно с этим сделать... а нельзя ли пользователей с количеством баллов выше определённого поставить в исключения спам-фильтра? По-моему, справедливо получится... а-то на странице форума Top Contributors впору заменить на Top Spamers :-)) |
| 16.07.2009 05:50
pdemenko
|
Re:Способ есть. В «три притопа» Вот здесь теория, которая легла в основу Intel(R) Thead Checker: http://delivery.acm.org/10.1145/1150000/1147416/p69-banerjee.....N=18648146 Если ошибка не детектится, значит Thread Checker не считает сегменты, в которых произошел доступ к данным, параллельными. Почему не считает - это вопрос к разработчикам, по всей видимости общий барьер, где синхронизируются потоки не является завершающей операцией для всех сегментов. |
| 16.07.2009 06:11
Vladimir Tsymbal (Intel)
|
Re: Назвать, как я уже сказал, - ошибка общего доступа к общей переменной (одновременное изменение). Гонка - это непредсказуемый результат работы множества параллельных процессов (см. для примера http://www.citforum.ru/programming/digest/multithreading/). При гонках процессы могут совсем не общаться между собой, а конечный результат может быть разным (см. приведенную ссылку) Что мы в данном случае имеем? Два, по сути, параллельных атомарных действия. Одно – увеличивает, второе – уменьшает значение общей переменной. Имея формально равную длительность, запущенные формально в одно и то же время, они должны одновременно изменить значение общей переменной. Что в этом случае должно быть в конечном итоге? Думаю, не знает и создатель данной программы! ИМХО, сам процесс доступа к общей переменной не может быть ошибкой, даже если он одновременный. Ошибочным (не соответствующим ожидаемому результату мат.модели) может быть результат изменения переменной, причем в зависимости от того, кто первый успел эту переменную модифицировать. Ошибки может и не произойти совсем, в зависимости от временных параметров потоков. Поэтому такой случай - классический пример гонки (data race), и ни как не "ошибки доступа". И уж тем более это ни какие не атомарные действия, по крайней мере с точки зрения микроархитектуры процессора (IA-32 и т.п.). А результат операции создателю очень даже известен, он просто неоднозначен :) |
| 16.07.2009 08:42
Dmitriy Vyukov
|
Re: Гонка - это непредсказуемый результат работы множества параллельных процессов... Именно. Как раз то, что мы тут и имеем. Как правильно заметил Vladimir Tsymbal, действия - не атомарные. Вообще гонки бывают на "разных уровнях", бывают как здесь - низкоуровневые, а бывают и в системах на основе обмена сообщениями типа Erlang. Бывают даже в синхронных системах. |
| 16.07.2009 08:45
Dmitriy Vyukov
|
Re: Способ есть. В «три притопа»:... Что-то типа такого я и имел в виду под "либо будет очень сложным и всё равно ненадёжным". К сожалению технологии построения и анализа моделей для произвольных бинарных программ сейчас нет. Иначе зачем вообще Инспектору запускать программу даже один раз - делов-то, восстановить, да проанализировать модель программы. |
| 16.07.2009 08:48
Dmitriy Vyukov
|
Re: прошу прощения, это спам-фильтр так вот неожиданно отработал У меня тоже не получалось 2 дня запостить сообщение сюда: http://software.intel.com/en-us/articles/using-locks-effecti.....amming-v4/ В итоге решилось отрезанием "http://" от ссылок. Re: а нельзя ли пользователей с количеством баллов выше определённого поставить в исключения спам-фильтра? Я - за :) |
| 16.07.2009 08:54
Dmitriy Vyukov
|
Re: Вот здесь теория, которая легла в основу Intel(R) Thead Checker Всё правильно: "Since we cannot expect to capture all rata races that may be present in Х...". Возможно тул не считает секции парраллельными, т.к. второй поток ещё не вышел в юзер-спейс, тогда как первый уже ушёл в ожидание второго. Если 2 потока одновременно находятся в ядре, то тул скорее всего должен считать это как точку синхронизации между ними. Кстати, Relacy Race Detector (http://groups.google.com/group/relacy) постулирует 100% нахождение *всех* гонок и других ошибок. Хотя, естественно, эти снижает область его применимости. |
| 16.07.2009 15:50
vlubch
|
Каждый имеет и/или может иметь свой взгляд на теорию и практику параллельных процессов :) Я придерживаюсь следующего: одновременное изменение общей переменной параллельными процессами – в общем случае грубейшая ошибка! Хотя тому есть примеры, когда это удобно. Но … опять же - это грубейшее нарушение принципов параллелизма. В оправдание этому можно сказать, что нет правил без исключений :) Но поясню «на пальцах» понятие «одновременности»:) Нельзя одновременно погладить и шлепнуть (здесь погладить и шлепнуть – атомарные действия). Можно либо погладить, а потом шлепнуть, или шлепнуть, а потом погладить. Глупо будет выглядеть воспитатель, пытающийся сделать это одновременно. Что он будет иметь реально: 1) сам себе отобьет руку, 2)погладит свою только что шлепнувшую руку (поощрит себя же любимого за то, что шлепнул, 3) сломает себе руки, пытаясь тупо сделать все одновременно :). В электронике гонки еще называют состязания сигналов. Применительно к программированию гонки – это состязание действий. Если конечный результат подобных «состязаний» разный от запуска к запуску, то это гонки, если нет – не гонки. В частном случае действия могут одновременно изменять общую переменную, а могут, спотыкаясь о что-то, делать это с небольшим сдвигом по отношению друг к другу. Если параллельная модель и/или ее практическое воплощение способствуют этому, то это очень плохо. В данном случае мы имеем пример, который убеждает в том, что многопоточность на такие вещи способна… А это уже повод призадуматься… Теперь об атомарности действий. В примере мы оперируем операторами языка С/С++, а потому и логично считать их атомарными действиями (в случае ассемблера это были бы отдельные команды). Хотя я лично предпочитал бы говорить о функциях, как атомарных действиях. Правда, в нашем случае это не столь и принципиально. |
| 16.07.2009 15:53
Alexey Kukanov (Intel)
|
> Возможно тул не считает секции парраллельными, т.к. второй поток ещё не вышел в юзер-спейс, тогда как первый уже ушёл в ожидание второго. Скорее в ожидание на барьере в конце параллельного региона. Можно попробовать проверить, либо заставив главный поток задержаться в начале параллельного региона, либо вообще синхронизировав потоки в начале региона. |
| 16.07.2009 16:00
vlubch
|
Re: Что-то типа такого я и имел в виду под "либо будет очень сложным и всё равно ненадёжным". К сожалению технологии построения и анализа моделей для произвольных бинарных программ сейчас нет. Иначе зачем вообще Инспектору запускать программу даже один раз - делов-то, восстановить, да проанализировать модель программы. Все правильно – зачем? :) А теории все же есть. Можно это представить на базе сетей Петри или на базе конечных автоматов. Любую бинарную программу можно привести к сети Петри или к автомату/сети автоматов. Дальше - работает теория анализа сетей Петри/автоматов. Правда сам анализ подобных моделей произвольных бинарных программ может быть очень сложным в силу большой сложности получаемых эквивалентных моделей, но формально это все те же … «три притопа» :) |
| 16.07.2009 16:27
Alexey Kukanov (Intel)
|
> Применительно к программированию гонки – это состязание действий. Если конечный результат подобных «состязаний» разный от запуска к запуску, то это гонки, если нет – не гонки. Конечный результат в данном случае может быть разным. Это гонка (впрочем, я тоже предпочитаю термин "состязание"). > В частном случае действия могут одновременно изменять общую переменную, а могут, спотыкаясь о что-то, делать это с небольшим сдвигом по отношению друг к другу. В общем случае, даже если две конкурирующие операции записи в память произошли на одном и том же такте процессора, физическое изменение состояния ячейки, я полагаю, произойдёт в разное время - со сдвигом на некоторое количество тактов. А если даже и нет, аппаратура должна позаботится об обработке такого конфликта. С моей точки зрения, можно считать, что изменения разнесены по времени. > Если параллельная модель и/или ее практическое воплощение способствуют этому, то это очень плохо. В данном случае мы имеем пример, который убеждает в том, что многопоточность на такие вещи способна… А это уже повод призадуматься… Да, многопоточная модель требует явной синхронизации доступа к разделяемой памяти. Ужас, ужас... :) > В примере мы оперируем операторами языка С/С++, а потому и логично считать их атомарными действиями (в случае ассемблера это были бы отдельные команды). Архитектура процессора ничего не знает об операторах языка С++. С точки зрения работы с памятью, атомарными будут операции чтения/записи в пределах выровненного машинного слова, а также операции изменения (RMW - read-modify-write), выполняемые специальными инструкциями. Операторы увеличения и уменьшения целочисленной переменной в языке C++ ни один нормальный компилятор не будет транслировать в такую инструкцию, потому что это в разы удорожает выполнение этой и других операций, и влияет на оптимизацию, как на уровне компиляции кода, так и на уровне аппаратуры. Поэтому g++ будет оттранслировано в "прочитать значение из памяти в регистр; увеличить регистр на 1; записать новое значение в память". Если требуется атомарность изменения, нужно либо явно синхронизировать доступ к переменной, либо указать компилятору, что он должен сгенерировать атомарную инструкцию. К сожалению, в языке C++ текущей версии стандарта не предусмотрено ни того, ни другого. Библиотеки поддержки параллельного программирования предоставляют для этого специальные программные интерфейсы, частично реализованные на ассемблере. |
| 17.07.2009 02:07
Dmitry Oganezov (Intel)
|
>> а нельзя ли пользователей с количеством баллов выше определённого поставить в исключения спам-фильтра? Технически - можно. Пытаюсь убедить нашу западную команду так и сделать. |
| 17.07.2009 03:11
Dmitriy Vyukov
|
Re: Я придерживаюсь следующего: одновременное изменение общей переменной параллельными процессами – в общем случае грубейшая ошибка... ... которая называется гонки/состязания. Re: Если конечный результат подобных «состязаний» разный от запуска к запуску, то это гонки, если нет – не гонки. Тут важно различать гонки и недетерминированные системы. Оба понятия до некоторой степени суть одно и то же, однако гонки - в общем случае плохо, недетерминированная система - в общем случае нормально. Допустим есть серверное приложение. Оно принимает запросы от клиентов и кладёт их в общий список, доступ к списку полностью корректно синхронизируется мьютексом. Так же можно запросить отчёт по всем запросам, в этом случае так же происходит корректная синхронизация с помощью мьютекса и содержимое списка считывается и выводится. В зависимости от множества факторов мы можем увидеть в отчёте либо (заявка1, заявка 2), либо (заявка2, заявка 1), либо (заявка1), либо (заявка 2). На лицо разный результат от запуска к запуску, однако, я надеюсь, никто не скажет, что это потому что в системе есть грубейшая ошибка. Важно различать, когда разный результат от запуска к запуску есть плохо, а когда - нормально. |
| 17.07.2009 03:14
Dmitriy Vyukov
|
Re: Теперь об атомарности действий. В примере мы оперируем операторами языка С/С++, а потому и логично считать их атомарными действиями Абсолютно не логично. В языке С++ нет "#pragma omp parallel for". Соотв. это - не ISO C++, а OpenMP поверх C++, поэтому смотри, что спецификация OpenMP добавляет поверх правил С++. В частности "#pragma omp atomic". |
| 17.07.2009 03:20
Dmitriy Vyukov
|
Re: А теории все же есть «три притопа» :) Ну это пожалуйста. Только Инспектору это нисколько не поможет, так же как и не поможет теоретическая возможность решать NP-сложные задачи любой размерности - перебрать все варианты и выбрать лучший... Кто-то предпочитает называть это "Теоретически Да". Я предпочитаю это называть "Нет", ну или "Теоретически Да, но практически Нет". Вопрос терминологии... |
| 17.07.2009 03:24
Dmitriy Vyukov
|
Re: К сожалению, в языке C++ текущей версии стандарта не предусмотрено ни того, ни другого... Ты забываешь, что в C++ и параллельного исполнения (потоков) не предусмотрено, так что никаких проблем не вижу ;) Любое расширение, которое предоставляет потоки (POSIX, Win32, CLI, boost, ACE, OpenMP, TBB, Cilk, ...), одновременно предоставляет и какой-то полный набор примитивов синхронизации. |
| 17.07.2009 06:12
Alexey Kukanov (Intel)
|
> в C++ и параллельного исполнения (потоков) не предусмотрено, так что никаких проблем не вижу ;) Согласен с первой частью поправки :) Проблема в том, что припозднились несколько с добавлением поддержки параллелизма на уровне языка и библиотеки. |
| 19.07.2009 07:38
vlubch
|
Re: Архитектура процессора ничего не знает об операторах языка С++… Думаю, множество программистов, пишущих на С++, если не знает, то не желает разбираться в тонкостях архитектуры. Если об этом думать, то переносимость программ будет сведена к нулю. Re: … ни один нормальный компилятор не будет транслировать в такую инструкцию, потому что это в разы удорожает выполнение этой и других операций, и влияет на оптимизацию, как на уровне компиляции кода, так и на уровне аппаратуры. Поэтому g++ будет оттранслировано в "прочитать значение из памяти в регистр; увеличить регистр на 1; записать новое значение в память". Я, как нормальный программист, хотел бы быть уверенным в том, что коли я дал команду увеличить переменную, то пусть «нормальный компилятор» именно это и соизволит сделать. Все остальное – регистры, оптимизация и т.п. – проблемы компилятора. Не я должен подстраиваться под него, а он должен строго выполнять мои указания. Я не хочу зависеть от его капризов. Здесь программист – жесткий диктатор, компилятор – беспрекословный исполнитель. |
| 19.07.2009 07:39
vlubch
|
Re: Если требуется атомарность изменения, нужно либо явно синхронизировать доступ к переменной, либо указать компилятору, что он должен сгенерировать атомарную инструкцию. К сожалению, в языке C++ текущей версии стандарта не предусмотрено ни того, ни другого. Библиотеки поддержки параллельного программирования предоставляют для этого специальные программные интерфейсы, частично реализованные на ассемблере. Если я программирую на С++, то для меня его операторы и есть атомарные команды. Миссия компилятора, среды исполнения и/или кого-то там еще – это обеспечить. Re: Проблема в том, что припозднились несколько с добавлением поддержки параллелизма на уровне языка и библиотеки. Проблема в том, что С++ - последовательный язык. Мы «припозднились» с созданием параллельных языков. А еще больше - с параллельной моделью вычислений. Ведь язык – это всего лишь средство описания алгоритмов в рамках той или иной модели. Если «добавления» формируют новую модель вычислений – одно, если нет – другой. Проблема в том, что многопоточность, а равно и многоядерность - не модели параллельных вычислений. Сети Петри, клеточные автоматы, нейронные и автоматные сети - примеры параллельных моделей. Если бы рассматривали, приведенный пример, как параллельный алгоритм в раках какой-либо модели, то многие проблемы, обсуждаемые здесь, вообще бы не возникали. Хотя бы та же «проблема атомарности». |
| 20.07.2009 07:42
Alexey Kukanov (Intel)
|
> Думаю, множество программистов, пишущих на С++, если не знает, то не желает разбираться в тонкостях архитектуры. Если об этом думать, то переносимость программ будет сведена к нулю. Для программистов, не желающих разбираться в тонкостях архитектуры, есть масса других языков. С++ и С изначально были языками, близкими к архитектуре и позволяющими максимально использовать возможности железа, и продолжают такими оставаться. Если об этом не думать, то производительность программ на целевой архитектуре будет значительно ниже, а это большинство волнует больше, чем проблемы переносимости. И уж точно это больше волнует производителей компиляторов и аппаратуры, потому что им надо свои продукты продавать. > Я, как нормальный программист, хотел бы быть уверенным в том, что коли я дал команду увеличить переменную, то пусть «нормальный компилятор» именно это и соизволит сделать. Так он именно это и сделает. "Никаких проблем не вижу", как сказал выше Дмитрий :) - с оговоркой на то, что язык, как вы выразились, последовательный. > Если я программирую на С++, то для меня его операторы и есть атомарные команды. Миссия компилятора, среды исполнения и/или кого-то там еще – это обеспечить. См. выше про ориентацию языка на производительность. К этому добавьте возможность перегрузки операторов, что делает пре- или постикремент функцией произвольной сложности. А вообще, что толку тут с нами спорить? Вам с этим предложением в комитет по стандарту надо. Я вполне серьёзно - вдруг да удастся там всех убедить? А если нет, то может быть им удастся в чём-то убедить вас. >Проблема в том, что многопоточность, а равно и многоядерность - не модели параллельных вычислений. Многопоточность - одна из моделей параллельных вычислений, хотите вы этого или нет. Не самая простая, с многими недостатками, но она существует, развивается, и готовит почву для создания новых, лучших моделей. И не отомрёт ещё долго, несмотря на все проблемы и недостатки. |
| 23.07.2009 08:21
Vladimir Tsymbal (Intel)
| Друзья, я тут собрался в отпуск, отдохнуть от всяких головоломок. По приезду подведу итоги дискуссии, поделюсь своими результатами, ну и конечно, назначим звание титана лучшему аналитику и блогерокоментатору, с поднятием флага и торжественным вручением командирских... э-э... в общем вручим обязательно, прямо перед строем! :) |
| 24.07.2009 01:19
vlubch
|
Отдых – дело просто чудесное, а, порой, и просто необходимое :). Ну, а мы тут пока продолжим, но не ради … «командирских», а токмо истины ради :) Re: …С++ и С изначально были языками, близкими к архитектуре… При том, что я не хотел бы вникать в архитектуру, меня полностью устраивает С++. Более того, есть вещи, которые я могу сделать, наверное, только на нем (но это не означает, что они архитектурно зависимы, а, скорее, наоборот). Да, в С++ заложены/включены возможности, которые для кого-то излишни, но они хорошо ложатся на ту или иную архитектуру. Но… архитектур много, а С++ один :) А потому упоминаемая операция g++ на разных компиляторах, ориентированных на ту или иную архитектуру/систему команд будет в конечном итоге транслироваться в разное число команд. И, возможно, когда мне понадобится, я выясню в какую, но в 99% (думаю, еще больше) лично я об этом не думаю. Если меня не устраивает скорость, то я в первую очередь ищу алгоритмический способ ее ускорить и только в самую-самую последнюю очередь оптимизирую операции. Поскольку первое будет работать всегда, второе – привязано к архитектуре. |
| 24.07.2009 01:19
vlubch
|
Re: Вам с этим предложением в комитет по стандарту надо. Если честно, то я не понял с каким (моим) предложением? :) Но комитету - комитетово, хотя … я бы хотел, чтобы С++ был параллельным (для меня, правда, он уже параллельный :) ) И, думаю, таким он когда-то станет, но для этого комитету нужно стандартизировать модель параллельных вычислений и на ее базе создать фактически новый параллельный С++ :) Re: Многопоточность - одна из моделей параллельных вычислений, хотите вы этого или нет… Я бы и хотел назвать ее (многопоточность) моделью вычислений, но … язык не поворачивается. И это никак не связано со мною :) По определению модель вычислений должна ВЫЧИСЛЯТЬ. Математики много поработали, дав определение вычислимой функции. Чтобы модель могла носить гордое звание модели вычислений она должна иметь 1) память, 2) набор операций, 3) иметь управление (например, операции управления). Вспомним в связи с этим понятие алгоритма, определение машин Поста и Тьюринга и т.д. и т.п… Включено ли в определении модели многопоточных вычислений (кстати, хотелось бы на такое определение посмотреть) хоть одно из этого? Хотя для полноценности должны быть все три элемента. |
| 24.07.2009 01:20
vlubch
|
Re: … Не самая простая, с многими недостатками, но она существует, развивается, и готовит почву для создания новых, лучших моделей. И не отомрёт ещё долго, несмотря на все проблемы и недостатки. Соглашусь лишь частично :) 1. Она не развивается. Как ее определили много десятилетий тому назад, так она и осталась. Что развивать-то (усложнять память? вводить новые операции? изменять управление?)? 2. И «почву» не готовит, т.к. не модель (см. выше об определении модели). На потоках, как машины по дорогам, «ездят» другие модели вычислений и не более того. 3. Ну, а то, что не отомрет – это, пожалуй, пока так. Но вот, когда комитет по стандартам утвердит новый параллельный С++, то, думаю, на этом эра многопоточности завершится :) Но не хотелось бы заглядывать столь далеко. Мы живем в это время и, видимо, многопоточности на наш век хватит. Поскольку даже если комитет сделает такой «подарок» прям сегодня, пройдет еще немало времени, чтобы он стал реальной жизнью. Поэтому как бы мне не хотелось другого, я признаю, что многопоточность будет еще жить. Сколько – вопрос другой :) Дороги-то пока тоже никто не отменял. Правда, наши в кое-где в таком состоянии, что поневоле думаешь, может, они уже и не нужны? Может уже есть «бездорожный» транспорт, иная «модель передвижения»? |
| 07.08.2009 04:33
Julia Fedorova (Intel)
|
Насчет первоначальной проблемы - не детектирования data race... (м.б. уже и так все разобрались - but just to make sure) Inspector детектирует и локализует (с точностью до строки кода) данную гонку но в случае когда (проверено на последней public версии - Update 1) 1. используется intel-овский openmp run-time 2. Inspector запускается с уровнем анализа 4 ("Where all the threading problems.. ") (это вот и бага - подобные проблемы он должен детектировать на уровне 2, и локализовывать с точностью до строки кода на уровне 3) |
| 24.08.2009 04:14
Vladimir Tsymbal (Intel)
|
Как же быстро и незаметно проходит отпуск... Разгадку я опубликовал в отдельном посте, куда и приглашаю вас ее пообсуждать. http://software.intel.com/ru-ru/blogs/2009/08/24/2002017/ |
| 16.10.2009 05:15
Andrey Karpov
|
Я понимаю, что пишу с большой задержкой, но возможно это будет кому-то интересно. Я сейчас занимаюсь созданием PVS-Studio 3.40, которая диагностирует ошибки в приведенном в начале примере следующим образом: simple.cpp(9): error V1205: Data race risk. Unprotected concurrent operation with the 'g_var' variable. simple.cpp(11): error V1205: Data race risk. Unprotected concurrent operation with the 'g_var' variable. Если интересно, предлагаю познакомиться с нашим инструментом по адресу: http://www.viva64.com/ru/pvs-studio/ P.S. доступная в данный момент на сайте версия 3.30 данную ошибку не обнаруживает. |
Обратная ссылка (1)
- Блоги Intel® Software Network » Разгадываем загадку многопоточности
24.08.2009 04:13





Dmitriy Vyukov
43,814
Возможность и вероятность диагностирования такой ошибки зависит от принципа работы. Если Inspector не использует целенаправленного управления шедулингом потоком на уровне отдельных операций, то ему будет очень сложно поймать такую ошибку, т.к. главный поток будет всегда успевать "проскочить" перед дополнительным рабочим потоком.