Блог пользователя antofik

Автор antofik, 11 лет назад, перевод, По-русски

Обнаружил интересное поведение в среде MS C# пока решал 415E - Машмох и операция реверс — добавление одной строки Console.Write("") неожиданно увеличило производительность на 30%.

Можно сравнить два решения 6429939 и 6429944, которые отличаются только одной этой строкой. Время выполнения для них соответственно 2105мс и 1777мс.

Несколько фактов:

  • вставка этой инструкции в начале программы не даёт эффекта
  • так же бесполезно оказывается вставить её непосредственно перед выводом результата
  • улучшение производительности заметно при больших объёмах данных (здесь ~1МБ)

Есть идеи?

UPDATE Вышеуказанные решения в данный момент стали работать подобным образом, что похоже на рандом. Но их "полная" версия до сих пор отличается более чем в два раза, что нельзя объяснить разбросом. 6430646 и 6430655

UPDATE2 Провёл ряд экспериментов, вот что получается:

  • в среде C# Mono проявляется тот же эффект
  • локально повторить не удаётся
  • отключение оптимизации с помощью атрибута MethodImpl приводит к нормальному исполнению программы
  • отключение оптимизации с помощью #pragma optimize( "", off ) не даёт никакого эффекта
  • замена Console.Write("") на Console.Out.Flush() или Console.In.Close() тоже даёт программе нормально выполняться
  • в одной из строк чтения данных по-ошибке использовалось лишнее приведение к списку, а потом к массиву Console.ReadLine().Split().Select(int.Parse).ToList().ToArray(). Убираем ToList() — и чудо, программа снова работает как положено (отдельно измерял время выполнения лишней процедуры — оно оказалось достаточно маленьким, и не объясняющим эффекта)
  • замена Interlocked.Exchange() на обычные операции присвоения так же приводит к нормальному выполнению программы

Возникает предположение, что операции чтения-записи могут блокировать Interlocked.Exchange, что каким-то образом возникает после оптимизаций компилятора. Поэтому эту функции следует применять с осторожностью. Непонятным остаётся что за оптимизации мог сделать компилятор, чтобы ввод-вывод смог заблокировать Interlocked.Exchange()

Исходники: подвисающее 6434408, без Interlock-a 6434414, без ToList() 6434416

  • Проголосовать: нравится
  • +11
  • Проголосовать: не нравится

»
11 лет назад, # |
  Проголосовать: нравится +13 Проголосовать: не нравится

Просто небольшой рандом. Посылка, идентичная первой, без строки. Отработала за 1793мс, примерно как вторая

  • »
    »
    11 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    Весьма странно — я сделал порядка 10 разных посылок с и без указанной строки — разница всегда подтверждается.

    P.S. Сделал ещё штук по 10 отправок каждого варианта — действительно, выглядит как рандом с +-20%. Но ведь два часа назад, когда я создавал пост, рандома не было, поведение чётко отслеживалось. В моём оригинальном решении эта строка сводила решение от стабильного TIME_LIMIT_EXCEEDED (а это >4с) к проходимому решению за 1.6с. И продтвердилось это с десяток раз. Вот, взял старое решение, отличается тоже одной строкой, сравните 6430646 и 6430655

    • »
      »
      »
      11 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      А как же 6430594.

      UPD Опоздал.

      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится +1 Проголосовать: не нравится

        Несмотря на то, что в "почищенной" версии разница в данные момент перестала проявляться, в "полном" варианте она до сих пор есть, и огромная. Для меня это выглядит пока что не понятно

        • »
          »
          »
          »
          »
          11 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          Если действительно такой интерес — надо смотреть MSIL код, там разница определится. А можно еще проще — в десятке следующих задач всегда добавлять эту штуку. А потом потестить их все снова без нее. Если хоть иногда помогает — пофигу, надо ставить всегда значит)

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится

            Я вот только что взял и посмотрел дизассемблированный код. Разница как раз только в наличии вызова функции Console.Write("");.

          • »
            »
            »
            »
            »
            »
            11 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится

            Скорее, надо смотреть исходники класса Console в .Net фреймворке. Только, подозреваю, чтобы добраться до сути, надо будет перелопатить еще очень много других классов. И то не факт, что проблема лежит там, а не в особенностях тестирования на серверах CF.

»
11 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

Эта тема уже обсуждалась, и все здесь очень просто. На CF несколько решений могут запускаться на одной тестирующей машине параллельно, отсюда как раз возникает разброс времени выполнения.

Предвкушая один вопрос, сразу на него отвечу: если решение получает TLE, то оно после этого запускается уже на отдельной машине (именно на этом тесте, или на всех — не в курсе).

»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

It is more interesting why did you try putting that dummy line in the code XD