вторник, 21 февраля 2017 г.

Оптимизация типового пути исполнения

Роясь недавно в исходниках TPL Dataflow я заметил некоторый паттерн: многие функции разбиты на две. Основная называется обычным образом, AddElement или что-то в этом роде, а вторая – AddElement_Slow.

При этом мне очень понравилась причина, по которой это дело делалось: эта оптимизация позволяет заинлайнить метод в типовом кейсе. Казалось бы, а есть ли в этом толк? И, как оказалось, что есть (я бы удивился, если бы камрад Тауб решил бы использовать подобную оптимизацию не проверив, что она имеет смысл).

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

Ну, и подробности, у меня в англоязычном посте: “A common execution path optimization”.

12 комментариев:

  1. В англоязычном блоге какой-то неправильный code highlighter - не добавляется горизонтальная прокрутка, поэтому с телефона, например, читать пост бесполезно.

    ОтветитьУдалить
    Ответы
    1. А в этом блоге хайлайтинг не так выглядит? Я посмотрел в мобильной версии этот и новый блог и разницы не увидел. Да, и там и там прокрутки нет, но это потому, что я не использую хайлайтеры, а копирую код из студии с сохранением форматирования.

      Удалить
  2. Интересная статья. Мне в ней нравится то что там нет каких-то прямо "хаков" для производительности - просто идея "вынести долгий и нечасто исполняемый код в отдельный метод" здравая сама по себе и не ухудшает читабельности и поддерживаемости.

    ОтветитьУдалить
    Ответы
    1. И еще вопрос - а заинлайнится ли
      if (not_enough_space)
      {
      Resize();
      }

      array[i] = val;

      где Resize вынесен в отдельный метод и вызывается нечасто?

      Удалить
    2. По умолчанию, лимит для инлайна - 20 IL инструкций. Кажется, что этот лимит не будет достигнут и в этом случае. Плюс, если метод будет вызываться из цикла, то данный лимит будет немного увеличен.

      Т.е. я думаю, что этот метод заинлайнится тоже.

      Удалить
    3. Конечно, гадание с (не) заинлайнится вещь всё же неочевидная. Я например далеко не настолько крут чтобы быстренько на глаз посчитать во сколько IL команд скомпилируется мой код.
      А есть какой-то способ для проверки - инлайнится метод или нет?
      Ну кроме ковыряния результирующего байт-кода руками.

      Удалить
    4. Тут даже не IL смотреть нужно, а генерируемый JIT-ом asm.

      Удалить
    5. Есть еще одна подобная фишка: если имеется здоровенный If-then-else-if-then-else... или switch - самые часто встречающиеся варианты выносить в начало списка.

      Правда, обязательно надо тестировать: компилятор может много чего наменять, плюс не уверен в соблюдении порядка switch-eм.

      Удалить
    6. Да, интересный момент. Как-то не думал в эту сторону, но частенько итак это делал.

      В приведенном примере реалокация/ресайз явно обладает достаточной ответсвенностью для выделения в отдельный метод. Хотелось бы отметить, что это не только не ухудшает читаемость, это улучшает читаемость, да и код "в общем". И я думаю, что зачастую можно выделить подобную ответственность у такого куска кода, а если так, то можно будет использовать вменяемый нейминг, вроде Reallocate/Resize etc. То, что в примере осознанно сохранен стиль нейминга я понимаю.

      Удалить
  3. Как можно выйти с Вами на связь? На этом сайте нашел только как комментарии оставлять. У меня есть к Вам предложение. Моя почта zemlyanikin@recursion.ru

    ОтветитьУдалить
  4. Thanks for sharing, nice post! Post really provice useful information!

    An Thái Sơn chia sẻ trẻ sơ sinh nằm nôi điện có tốt không hay võng điện có tốt không và giải đáp cục điện đưa võng giá bao nhiêu cũng như mua máy đưa võng ở tphcm địa chỉ ở đâu uy tín.

    ОтветитьУдалить