Советы разработчика: если результаты прогонов модели не воспроизводятся


Мы в команде техподдержки часто получаем от пользователей вопрос: что делать, если результаты прогонов модели не воспроизводятся. Если в модели есть какие-то источники стохастики, но вы не можете их определить – вот наш чек-лист для их поиска.

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

Для всех экспериментов

  1. В модели должны отсутствовать HashMap, HashSet, либо не должно быть итерирования по этим коллекциям. В силу специфики внутренней реализации этих коллекций, порядок итерирования зависит от значений hashCode() объектов. Если у объектов метод hashCode() не переопределён, то он возвращает произвольное значение (которое может зависеть от адреса объекта в памяти).
  2. В модели не должно быть вызовов методов object.hashCode() и System.identityHashCode() по причине, описанной в п.1.
  3. В модели не должно быть влияющих на логику вызовов, типа System.currentTimeMillis(), new Date() и т.п. Эти методы возвращают текущую дату компьютера.
  4. Если результаты воспроизводятся только после закрытия окна модели и повторного запуска, но не воспроизводятся после комбинации “запуск->остановка”, это может говорить о том, что каждый прогон модели оставляет лишние записи в пользовательских данных эксперимента. Частным случаем являются статические переменные, которые могут быть записаны первым прогоном и считаны вторым. "Лишние данные" от первого прогона используется вторым прогоном (т.е. у второго прогона другие исходные данные).
  5. Если в модели есть внешние источники данных (например, текстовый или Excel файл, БД), то нужно убедиться, что с этими данными не повторится ситуация из п.4: во внешних источниках ввода\вывода данных могут записываться "лишние данные".
  6. В модели не должны использоваться методы с неконтролируемым значением класса Random, например: Collections.shuffle() или Math.random(). В таких случаях объект класса Random должен быть заменён на getDefaultRandomGenerator().
  7. Нужно проверить свойства модельного времени в эксперименте: если не указана начальная дата, значит стартовая дата в исполняющем модуле (дата всегда указывается) будет выбрана как "текущая дата/время компьютера". Из-за этого некоторые методы конвертации таймаутов из обычных единиц времени в модельные могут давать разные результаты в силу ошибок при округлении.
  8. Если в модели создаются другие параллельные потоки, нужно убедиться, что их выходные данные корректно синхронизованы и упорядочены. Причина: потоки могут "обгонять" друг друга.
  9. Если всё вышеперечисленное не помогло, то стоит попробовать отключить анимацию модели (например, создать Нестандартный эксперимент или запустить модель, не переключаясь на панель Main; выставить фиксированную частоту кадров). Возможно, на динамических свойствах какой-либо из фигур написан вызов функции, которая что-то меняет в модели? Это может быть другой метод getMyX(), который прописан как код для X в фигуре, или "On Draw" у группы. Любой код в динамических свойствах фигуры просчитывается на каждом кадре. Условно говоря, если первый прогон был выполнен с FPS 50, а во время второго опустился до FPS 30, то во втором прогоне наша функция будет вызвана на 20 раз меньше.

Для экспериментов с множественными прогонами (в основном это в нестандартный эксперимент).

    Нужно убедиться, что агент верхнего уровня "Main" не оставляет "лишних данных": не должно быть статических переменных, не должно быть переменных/полей в классе эксперимента, в которые бы Main что-то записывал. Не должно быть иных способов передачи данных между прогонами (включая базу данных).
  1. Если вы используете нестандартный генератор случайных чисел, то нужно убедиться, что он перезапускается/сбрасывается перед каждым прогоном модели (т.к. ГСЧ "хранит состояние", числа генерируются в потоке, и очередное значение зависит от того, что и сколько взяли из ГСЧ до этого).
  2. Если вы скопировали значения параметров (например, оптимизации) из консоли и вставили в код эксперимента, то убедитесь, что числа имеют максимальную точность (хотя хорошая модель не должна страдать гиперчувствительностью к далёким знакам после запятой в значениях параметров).
  3. Распараллеленный эксперимент может давать различные результаты из-за непредсказуемых обгонов одних потоков другими (в основном это к относится к оптимизатору: он ищет дальнейший путь в зависимости от поступающих от модели результатов).
  4. Убедитесь, что совпадают такие параметры исполняющего модуля, как время/дата старта и окончания моделирования, режим генерации случайных чисел, режим срабатывания одновременных событий, ограничений и т.п.

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