Сбой программы RNG на С++

В настоящее время я пишу roguelike и, естественно, использую много генерации случайных чисел.

Проблема, с которой я сталкиваюсь, заключается в том, что если я "перегрею" rand(); моя программа рухнет.

Если я генерирую только 20 или около того целых чисел за кадр, все в порядке... но когда количество случайных чисел достигает сотен, программа вылетает. Чем больше я создаю каждый кадр, тем быстрее он падает... что наводит меня на мысль, что происходит какой-то пайлап.

Я провел тесты и на 20 rand(); вызовов на кадр, он будет работать 24 часа подряд на максимальной скорости без сбоев. Втрое больше, и это не составляет и десяти минут.

Если я поставлю srand(); при инициализации я могу создать тысячи случайных чисел, прежде чем он заблокируется, но если я поставлю srand(); внутри самого кадра я делаю примерно 2-8 кадров. Если это имеет значение, я использую time(null) для раздачи.

чем чаще я вызываю rand(); тем быстрее он рухнет.

Помощь?


person motioneffector    schedule 04.04.2011    source источник
comment
Вы уверены, что он падает в реализации rand()? Вы использовали отладчик, чтобы подтвердить это поведение? Даже если это так, если вы не можете воспроизвести проблему в простом случае (int main(void) {while(1) rand(); }), я бы не заподозрил ранд в качестве вашей проблемы.   -  person Yann Ramin    schedule 05.04.2011
comment
Покажите нам минимальный неудачный пример   -  person sehe    schedule 05.04.2011
comment
Вы звоните rand из нескольких потоков?   -  person Null Set    schedule 05.04.2011
comment
Удалось ли вам воспроизвести это в отладчике и посмотреть, где и почему происходит сбой? Или, если использование отладчика нецелесообразно, вы настроили его для создания аварийного дампа, а затем проверили его, чтобы увидеть, где произошел сбой? Может быть, вы списываете конец массива или имеете висячий указатель или что-то подобное, что может повредить память и, таким образом, косвенно вызвать сбой этих библиотечных процедур?   -  person QuantumMechanic    schedule 05.04.2011
comment
Вероятно, вы сделали что-то в своем коде, что вызывает неопределенное поведение. Можете ли вы воспроизвести сбой в фрагменте кода, который ничего не делает, кроме многократного вызова rand() и srand()?   -  person Jim Lewis    schedule 05.04.2011
comment
Хорошая мысль о возможности многопоточности. Подробнее об этом читайте в этой статье о безопасности потоков и RNG.   -  person QuantumMechanic    schedule 05.04.2011
comment
Я не использую многопоточность — единственное, что я делаю с указателями, — это перебор std::set и выполнение (*it)->run(); к классам в наборе.   -  person motioneffector    schedule 05.04.2011
comment
Сбой внутри вызова rand?   -  person Null Set    schedule 05.04.2011
comment
Я не уверен - мой отладчик плохо работает. Я просто знаю, что после разного количества оборотов программа зависает. Я удерживаю клавишу следующего поворота, и каждый ход монстры блуждают по сетке в случайном направлении. Чем больше у меня монстров, тем быстрее он блокируется - а ниже определенного, небольшого количества монстров он никогда не запирается... поэтому я подозреваю, что это какая-то проблема со сборкой мусора.   -  person motioneffector    schedule 05.04.2011
comment
Единственные вещи, которые используют указатели, — это механизм поворота, который перебирает экземпляры класса монстров std::set, и различные функции SDL, которые полагаются на указатели. Я освобождаю все свои SDL-поверхности и так далее, так что это не проблема.   -  person motioneffector    schedule 05.04.2011


Ответы (5)


Несколько комментариев и идей о том, как сузить источник проблемы:

  • Почти наверняка это не функции srand() или rand(), вызывающие сбой/зависание. Скорее всего, одна или несколько комбинаций случайных чисел переводят ваш двигатель в состояние, когда происходит что-то плохое.
  • Первым шагом должно быть дублирование проблемы, чтобы она всегда происходила в то время/в месте. Вместо использования srand(NULL) попробуйте использовать постоянное начальное число, например srand(12345). В зависимости от того, какие другие факторы использует ваш движок (например, пользовательский ввод), этого может быть достаточно, чтобы он каждый раз вылетал в одном и том же месте.
  • Если при использовании отладчика возникают проблемы (что вызывает подозрение, возможно, переполнение буфера приводит к повреждению стека), используйте проверенный метод вывода сообщений в текстовый файл журнала. Я бы предложил вывести все сгенерированные случайные числа, и, возможно, вы увидите шаблон, когда он падает (т. Е. Он падает всякий раз, когда генерируется «42»). Другой вариант — начать добавлять несколько сообщений журнала в различные функции (начните с функций высокого уровня, таких как цикл обновления игры). После сбоя проверьте журнал и начните добавлять новые сообщения журнала, пока не сузите его до одной строки/функции. Это не так быстро, как использование отладчика, но иногда это лучший выбор, особенно если вы действительно не знаете, с чего начать поиск.
  • Как только вы сможете надежно воспроизвести сбой, начните удалять вещи, пока точка сбоя не изменится или не исчезнет. Это может включать в себя #ifdef, комментирование кода, настройку параметров приложения или даже создание временной копии проекта, чтобы вы могли просто удалить код, скомпилировать и протестировать. Это может быть сложно, если проект большой/сложный.
  • Было бы полезно получить дополнительную информацию о типе «аварии». Обычно программы не просто аварийно завершают работу, но возникают определенные исключения, блокировки и т. д. Детали исключений могут помочь вам сузить источник проблемы, приложив некоторые усилия.
person uesp    schedule 04.04.2011
comment
Я нашел ошибку, и это было то, о чем я даже не догадывался - враги оказывались в окружении других врагов, что приводило к бесконечному циклу их сценария движения и аварийному завершению. в то время как если бы они были окружены стенами, они просто оставались бы на месте, по какой-то причине окружение другими объектами не вызывало бы случай остановки по умолчанию, поэтому проверка соседних квадратов на наличие цикла столкновений продолжалась бы вечно - отсюда всегда разная длина время до аварии. Это был специфический, уникальный, придирчивый случай - но неизбежный, и, скорее всего, с большим количеством врагов. - person motioneffector; 05.04.2011

Функция rand() не является реентерабельной или потокобезопасной, поскольку использует скрытое состояние, которое изменяется при каждом вызове. Это может быть просто начальное значение, которое будет использоваться при следующем вызове, или это может быть что-то более сложное. Чтобы получить воспроизводимое поведение в многопоточном приложении, это состояние необходимо сделать явным. Функция rand_r() снабжена указателем на беззнаковое целое число, которое будет использоваться как состояние. Это очень небольшое количество состояний, поэтому эта функция будет слабым генератором псевдослучайных чисел. Вместо этого попробуйте drand48_r(3).

person sehe    schedule 04.04.2011
comment
У меня была такая же идея, но, видимо, это однопоточная программа. - person Null Set; 05.04.2011

Попробуйте запустить под отладчиком

$ gdb myprog
(gdb) break main
(gdb) run
(gdb) record

e.g.

(gdb) break abort
(gdb) break exit

так как это С++:

(gdb) catch throw
(gdb) catch exception

и наконец (gdb) продолжить

Когда он остановится, продолжайте движение в обратном направлении, пока не найдете виновника.


Вариант 2:

valgrind --tool=massif --massif-out-file="massif.out.%p" myprog
ms_print massif.out.*

изучить профилирование кучи. Не исключено, что у вас утечка памяти

person sehe    schedule 04.04.2011

Возможно, большое количество вызовов rand приводит к числу в относительно небольшом диапазоне, который ваш код не может обработать. Попробуйте заменить вызовы rand функцией, которая просто увеличивает число и возвращает его, и посмотрите, не сработает ли она в конечном итоге.

person Null Set    schedule 04.04.2011

  1. Вероятно, вам не следует использовать rand(). Есть гораздо лучшие PRNG. Взгляните на Boost.Random.
  2. Вы должны использовать srand() только один раз, а не каждый кадр.
  3. Узнайте, где ваш код дает сбой. Используя довольно простой отладчик, просто запустите программу с подключенным отладчиком и подождите, пока она не выйдет из строя.
  4. После того, как вы узнали, где он падает, выясните, почему он падает.
  5. После того, как вы узнали, почему, исправьте это. Вероятно, это не имеет ничего общего с rand().
person Paul Groke    schedule 04.04.2011