Pull to refresh

Прощай, if $DEBUG!

Reading time3 min
Views3.2K
Думаю, любой программист на Perl довольно регулярно добавляет в программу вспомогательный код, который не должен выполняться всегда. Это может быть отладочный код, сбор статистики о скорости работы разных частей кода, вывод в лог, и т.д. С этим кодом связано сразу несколько проблем:
  1. Он мешает читать основной код.Он замедляет выполнение программы.Из-за первых двух причин его зачастую удаляют, как только необходимость в нём пропадает… только, к сожалению, необходимость в нём регулярно возникает снова, и этот код, матерясь, снова пишут… чтобы через несколько часов снова удалить.Борьба с первой проблемой, как правило, обречена на неудачу. Ибо если код должен выполняться, то он должен быть написан. А если он написан, то он царапает глаза, разрывает основной код, раздувает код, отвлекает и раздражает. Решить эту проблему, как правило, удаётся только тогда, когда этот код должен быть написан в самом начале и/или конце функции — тогда можно автоматически сгенерировать функцию-обёртку, которая спрячет внутри себя этот код.

    А вот со второй проблемой бороться можно вполне успешно:
    warn "i=$i\n" if $DEBUG;
    В этом случае потери производительности когда $DEBUG==0 ограничиваются проверкой одного скаляра, т.е. фактически код получается такой:
    0;

    К сожалению, это решение приводит к тому, что «лишний» код становится ещё больше: в каждой такой строке добавляется if $DEBUG. С этим пытаются иногда бороться, перенося этот if внутрь вызываемой функции:
    sub log { return if !$DEBUG; warn "$_[0]\n"; }
    log("i=$i");

    К сожалению, производительность при этом падаёт значительно сильнее, т.к. к проверке скаляра добавляется ещё и вход/выход в функцию. А поскольку смысл существования $DEBUG в том, чтобы производительность не падала, то этот способ обычно не пользуется популярностью.

    Ещё один вариант — использование технологии source filters. С её помощью можно изменить код программы перед тем, как perl начнёт её компилировать и выполнять. Изменить как любой другой текст, обычно с помощью регулярных выражений. Например, код превратить в комментарии, либо наоборот — это позволит полностью избежать замедления программы (например, посмотрите модуль Smart::Comments). Первая проблема этого подхода в том, что синтаксис языка Perl очень сложный, а регулярные выражения использующиеся для модификации кода программы (обычно довольно простые) иногда ошибаются… и искать и исправлять такие баги довольно сложно. Вторая проблема заключается в том, что появляется ощущение потери контроля над своим кодом — вы уже не знаете точно, какой именно код выполняется, т.к. ваш код был как-то модифицирован. Парадокс, но вторая проблема полностью сводит на нет смысл существования таких модулей — ведь они изначально появились чтобы облегчить работу с кодом убрав из него «лишние» команды, а в результате работа с кодом наоборот, усложнилась.

    Я хочу предложить вашему вниманию другой способ решения этой проблемы:
    BEGIN {
      *log = $ENV{DEBUG} ? sub {warn "@_"} : sub () {0};
    }
    log+ "data", 123;

    Идея в том, что при отключенном отладочном режиме функция log определяется как константа, а функции-константы perl оптимизирует и просто подставляет в код их значения вместо того, чтобы их вызывать. (Код BEGIN { *log = sub () {0} } идентичен коду use constant log => 0;.) В результате реальный код, который выполнит perl будет:
    log( +"data", 123 ); # if $ENV{DEBUG}
    0 + "data", 123;     # if !$ENV{DEBUG}

    В результате от if $DEBUG мы избавились, и функция не вызывается, когда отладочный режим выключен. (А когда отладочный режим включен — лишний унарный плюс никак испортить первый аргумент функции не должен.)

    К сожалению, у этого изврата есть два побочных эффекта. Первый — use warnings ругается на попытки сложения не-чисел. Второй — этот способ всё же немного медленнее варианта с if $DEBUG, т.к. аргументы функции log вычисляются даже в том случае, когда отладочный режим отключен.

    Впрочем, несмотря на недостатки, этот способ имеет право на существование, и, возможно, будет кому нибудь полезен. К тому же, придумал я его всего пару часов назад, и возможно его ещё удастся развить и избавить от недостатков.
Tags:
Hubs:
Total votes 15: ↑13 and ↓2+11
Comments36

Articles