Pull to refresh

Comments 36

Как вариант, вместо унарного плюса можно использовать амперсанд, при условии что первый аргумент функции - другая функция или константа:

use constant INFO => 1, ERR => 2;
log &INFO, "message";
log &ERR, "message";
UFO just landed and posted this here
Угу. Только логики приложения за этим мусором уже не видно давно, а так всё в порядке...
Не поможет что? Вести логи с разной степенью детализации? Замерять скорость работы разных участков кода? Нет, не поможет...

Более того, большинство проблем лично мне удобнее и значительно быстрее отлаживать вставляя в код warn-ы, нежели в отладчике. Да, я понимаю, звучит дико и пахнет седой древностью, но... это реально удобнее и быстрее во многих случаях.
А нельзя если дебаг равен труЪ, то что-нить заинклюдить? Не верю что от одного условия вся производительность упадёт.
Проблема в том, что код, который нужно "заинклюдить" должен быть равномерно распределён по всему коду приложения. Ну, представьте себе, к примеру, вывод в лог. Для этого по всему приложению нужно в ключевых местах распихать команды вывода в лог, указывая везде разные log level и данные, которые нужно вывести в лог конкретно в этом месте. И как вы предлагаете все эти команды "заинклюдить"? Не родился ещё такой мощный инклуд, чтобы взять команды из одного файла и равномерно распределить их по другому, в нужных местах. :)
Да, чёто ступил, сам же дебажу так как вы пишите.
ИМХО: Хотя вызов функции лог тоже помойму жрёт немного, а её вот инклудить если дебаг==труЪ а если иначе то создавать функцию — пустышку. Обычно логаются же уже существующие переменные, а не мега красивый лог верстается, чтобы потом друзьям показать «зацените, как я красиво дебажу»… :)
На этапе компиляции Перл вычисляет все константные условия, так что вся статья, честно говоря, ни к чему - писать:
warn 'something' if DEBUG;
когда DEBUG объявлено как
use DEBUG => 0;
равносильно пустой строке, а когда
use DEBUG => 1;
просто warn 'something';

На этапе выполнения 0 и 1 не вычисляются - условие просто отбрасывается!

Это было в одном из рецептов отладки под mod_perl в mod_perl Cookbook.
Суть статьи была в том, чтобы избежать замусоривания кода текстом "if DEBUG".
Но ведь вы фактически предлагаете заменить одно выражение на другое. На самом деле вы просто оптимизировали вариант с вызовом функции. Я бы все таки отдал предпочтение фильтрам и сложность синтаксиса в таком случае не должна пугать, а иначе в сад .. т.е. может быть python/php (как вариант)? ;)
1. классический вариант ничем не хуже:
perl -MO=Deparse -e 'sub DEBUG() {0};warn "DEBUG" if DEBUG;warn "!DEBUG" if !DEBUG;'
perl -MO=Deparse -e 'sub DEBUG() {1};warn "DEBUG" if DEBUG;warn "!DEBUG" if !DEBUG;'
2. ваш вариант тоже неплох, только вот ошибочка:
не будет 0+"data", а 0, (т.е. операция + не будет выполняться в рантайме) ну и варнинг в нагрузку.
perl -MO=Deparse -e 'BEGIN{*lg=sub(){0}} lg+ "data", 123;'
  1. У классического варианта свои недостатки. Во-первых в каждой строке болтается "if DEBUG". Во-вторых, когда нужно управлять, например, детализацией лога, if-ы становятся ещё и разнообразными, что тоже не идёт на пользу читабельности:
    log(INFO,"message") if LOG_INFO;
    log(ERR,"message") if LOG_ERR;

  2. Ещё точнее, вместо 0+"data" будет не 0, а "data". :) Я просто не видел смысла вдаваться в такие детали в статье, там важнее было описать какие эффекты будут в плане производительности, чем точно указать генерируемые perl-ом опкоды, IMHO.
Кроме извращенных решений perl предлагает более тривиальные способы управления кодом: рекомендую perldoc perlfilter, конкретнее Filter::cpp, хотя мне больше по душе встроенный препроцессор :) транслятор конфигурации за пару минут IMHO каждый может накидать.
Я про source filters в статье писал - в частности, указал на очень серьёзные, с моей точки зрения, недостатки.
Честно, не вижу тех проблем, которые Вы описываете. Боязнь потери контроля над кодом — это проблема не фильтров, а хрупкого кода. Ну а про ошибающиеся регулярные выражения даже говорить как-то странно. Но если Вы видите в этом проблему: можно для предотвращения ошибок выработать однозначные соглашения по меткам в коде.
Почитал исходники фильтров (http://search.cpan.org/~pmqs/Filter-1.34/), не вижу никаких проблем, поправьте меня, может я что-то фундаментальное упускаю?
Угу. Упускаете. Как я уже говорил, синтаксис Perl довольно сложный, и написать корректный парзер очень тяжело. Возможно Вы слышали, что: "only perl can parse Perl". :)

Как Вы думаете, что выведет вот этот код:

use Filter::cpp;
#define DEBUG 1
#define STATUS (DEBUG ? "enabled\n" : "disabled\n")
warn "DEBUG mode: ".STATUS;
warn 'DEBUG mode: '.STATUS;
warn q{DEBUG mode: }.STATUS;
warn qq{DEBUG mode: }.STATUS;

А вот что:

DEBUG mode: enabled
DEBUG mode: enabled
1 mode: enabled
1 mode: enabled

И происходит эта лажа исключительно потому, что для perl и '' и "" и q{} и qq{} - это текстовые строки, а для Filter::cpp текстовыми строками являются только первые два варианта - иными словами Filter::cpp некорректно парзит Perl. И так обстоят дела с любыми source filters.
(Да, теоретически возможно написать корректный парзер Perl на Perl для использования в source filters, но во-первых он будет очень не быстро работать, а во-вторых будет нереально тяжело поддерживать его совместимость с самим perl.)

Так вот, упомянутая мной в статье проблема заключается как раз в том, что программист использующий source filter может быть абсолютно точно уверен, что этот source filter некорректно парзит Perl, но предположить ГДЕ и КАК он лажанётся - невозможно! Поэтому приходится при использовании source filter стараться использовать минимум возможностей perl, и всё-равно нет гарантии что он где-нить не лажанётся. А когда он лажанётся, найти этот баг будет крайне тяжело - ведь Вы не видите тот код, который реально выполняет perl после отрабатывания source filter.
Это еще больший изврат, чем предложенная функция в статье!
Конечно если ходить по полю, с разложенными граблями можно убиться. Зачем использовать управляющие конструкции в исполняемом тексте? Абсолютной свободы не бывает.
Задачу, которую Вы ставили перед собой — это повышение читаемости текста и отсутствие падения производительности . Первое решается на ура, любой редактор выделит эти конструкции цветом, они сразу станут блочними и будут выразительны. Второе имеет смысл только при частых запусках скрипта, что при использовании ускорителей или при работе демона не имеет смысла.
В perl-е мне в очень нравится, что "все уже написано до нас", зачем изобретать велосипед, который ездит не по требованиям? Ваш вариант не решает этих проблем.
Я довольно часто пишу код для сложного асинхронного I/O, который должен работать очень быстро. Например код, который выкачивает 500 url/sec, выполняет довольно много операций при выкачке каждой url. Эти операции в отладочном режиме могут выводить в лог десятки сообщений. А это значит, что число вызовов функции log() измеряется десятками тысяч в секунду. Добавьте ко времени непосредственно самого вывода в лог, время на подготовку и оформление выводимых в лог данных. И получится, что скорость системы падает в разы, если не на порядок. Вот и приходится выкручиваться...
Я согласен, бывают очень требовательные приложения и не менее требовательные люди :)
Фильтры мне самому не очень нравятся, я предлагаю использовать встроенный препроцессор. Т.е. при разработке Вы пишете #define и перед стартом транслируете в рабочий вариант с помощью Deparse.
Да, вариант любопытный, я его рассматривал. Но меня смущало следующее:
  1. получается несколько версий окончательного кода (смотря с какими директивами его генерить)
  2. после каждого изменения кода требуется запуск препроцессора
  3. в инете есть отличные статьи с описанием "чем плохо использовать препроцессор" (они писались для C, но принципы одинаковы для всех языков)
  4. нет возможности "на ходу" включать/выключать логи или отладочный режим (например, послав процессу сигнал)
1. Для той же VS это нормально — Debug, Release, сам выбираешь, что сейчас компилится.
2. Не обязательно, это же обычные комментарии
3. Не читал, обязательно пороюсь
4. Тут ничего не поделаешь, или два модуля держать, что не очень удобно, нужна дополнительная структура для управления подключением :(

Но все равно, использовать новую конструкцию, которая по сути является хаком, не есть гуд. Люди привыкли к if $DEBUG, это нормально, чем меньше надо помнить — тем быстрее работаешь.
Кстати, насколько выигрыш log перед $DEBUG?
Нинасколько, оно медленее, за счёт того, что приходится вычислять параметры функции, даже при том, что она не вызывается.

Я согласен насчёт хака, и насчёт непривычности. Просто неожиданно пришёл такой вариант в голову, решил запостить - может кому пригодится, или вместе на его базе что-нить более эффективное придумаем.

По поводу 3 - попробовал сейчас найти ссылки... немного на эту тему нарыл, но не очень убедительного. А того, что я когда-то читал - не вижу. :(
Ога, статей путевых не нашел, иногда поминают "кастыль", но без развернытых пояснений. Припомнилось, как читал, уже давненько, про подводные камни в выражениях для препроцессора. Их там много :)

Рад, что Вы признаете это хаком :) Такой код интересен в академическом смысле, а для индустриальной разработки даже более чем вреден. Судя по профилю у Вас много народу под началом, если каждый будет писать хаченый софт это увеличит общую производительность труда? Возможно на стадии конструирования, а на стадии сопровождения это встанет в копеечку.

Усложнять просто, упрощать сложно (с)
Удачи :)
Не, в компании это двигать вообще толку нет (я не про данный хак, конечно, а в принципе про разные правильные вещи). Среда сопротивляется из всех сил, уходит море времени чтобы чуточку изменить ситуацию в более правильную сторону. Так что на это я практически забил, и стараюсь сам правильно работать, а не другим мозги промывать.
http://log4perl.sourceforge.net/ в помощь =)
Ненавижу bloatware!

Log::Log4perl в 2.5 раза медленнее моей аналогичной реализации и в 16 раз медленнее if DEBUG.
Ваша реализация может в сислог писать и в собственные файлы?
откройте для себя Smart::Comments и не страдайте ерундой :)
Если бы Вы прочитали статью целиком, Вы обязательно заметили в ней ссылку на этот замечательный модуль.
Есть ещё такой вариант, скорость такая же, как при if (DEBUG) , а читабельность выше.

use constant DEBUG => 0;
DEBUG && warn "i=$i";
imho лучший способ. И подозреваю быстрее чем if
От проверки $DEBUG оверхед просто микроскопический. Ваша микрооптимизация только прибавит очередной пункт в code complexity
1. это не $DEBUG , а
sub DEBUG {1} # или sub DEBUG {0}
хотя, конечно же $DEBUG так же подойдёт.
2. лично мне кажется, что строка начинающаяся со слова "DEBUG" самая понятная для обозначения отладки.
3. Т.к. я всегда пишу IF одним и тем же способом, такая запись экономит лично мне 2 строки и часть символов
4. Да, это слегка сложнее для понимания, т.к. эта запись не стандартизирована, хотя и является полным аналогом IF(){}
P.S. IF я пиши так:
if(DEBUG)
{
    warn "i=$i";
}
Sign up to leave a comment.

Articles