Источники недетерминизма
Моя якобы детерминированная программа выдает один из нескольких слегка отличных результатов на разных прогонах. Вход, компилятор и компьютер неизменны. Я не уверен, какой вывод правильный, потому что он всегда выглядит разумным.
Помимо случайного вызова rand(), как это возможно?
12 ответов
Несколькими способами:
- использование нескольких потоков таким образом, что включает гонку данных,
- используя текущее системное время в качестве входных данных,
- используя неинициализированные переменные,
- ...
Мы, конечно, можем сделать больше предположений, но если вы хотите получить значимую помощь, возможно, было бы неплохо опубликовать соответствующие части вашего кода:-)
Если ваш вывод зависит от адреса, выделенного в куче:
int main(int argc, char* argv[])
{
printf("%p", malloc(42));
return 0;
}
Для каждого запуска malloc() может возвращать другой виртуальный адрес - не говоря уже о NULL в случае сбоя выделения.
Возможно:
- Нить время
- Любой вид ввода (пользователь, файл, сеть и т. Д.)
Если ваша программа использует float / double, результат может быть разным, если в какой-то архитектуре есть переключение контекста.
В x86 FPU использует расширенную точность для промежуточного результата, но при сохранении в памяти (что происходит при переключении контекста либо в процессе, либо в потоке) такая точность теряется. Это может привести к небольшому расхождению в результате (мы обнаружили такую проблему в нашей программе). Один из способов избежать этой проблемы - попросить компилятор не использовать FPU, а SSE для операций с плавающей запятой.
http://www.network-theory.co.uk/docs/gccintro/gccintro_70.html
Помимо случайного вызова rand()
rand()
является полностью детерминированным, если вы кормите его тем же начальным семенем.
Не видя некоторого кода (подсказка), лучшее, что я могу придумать, - это поиск шаблона. Может быть, что-то конкретное время и дата.
Кроме того, попробуйте поискать условия гонки. Это может выглядеть недетерминированным.
Использование значения указателя вместо того, на что он указывает, всегда дает интересные результаты.
В программах, которые не сильно взаимодействуют с "внешним миром", популярным источником недетерминизма является зависимость от указателя. Время от времени вы можете видеть это в коде: когда в лексикографической функции сравнения не хватает вещей для сравнения (все совпадают), она сравнивает адреса объектов как последнее средство. Это может привести к разным порядкам, если объекты размещены в динамической памяти, поскольку фактические места размещения могут различаться для разных платформ и для разных прогонов.
Вы не дали много информации. Однако, как человек, который занимается программированием в реальном времени для жизни, наиболее вероятные виновники, которых я ищу, когда такие вещи случаются:
- Использование неинициализированной памяти.
- Состояние гонки.
- Некоторая неясная комбинация вышеперечисленного.
Например, одна такая проблема, с которой я когда-то сталкивался, заключалась в том, что разделяемая библиотека не была такой "общей", как я думал, и пыталась использовать дескриптор одного процесса для индексации таблицы, которая еще не была инициализирована во втором процессе. В зависимости от того, как все началось, что могло или не могло вызвать сбой важных данных в третьем процессе.
Любое неопределенное поведение. То есть: потребуются сотни страниц, чтобы объяснить все возможные источники изменения выходных данных. Попробуйте отладку, чтобы найти, где происходит изменение, или читайте некоторые спецификации C++.