В связи с некоторыми событиями будет лучше, если появится тема, в которой я разберу несколько типичных ошибок горе-оптимизаторов, вводящих остальных в заблуждение.
Ошибка первая: "Нетабулированный код работает медленнее и хуже"
Один из наиболее частых советов для "оптимизации" кода - его табулирование. Дескать, нетабулированный код и собирается хуже, и работает медленнее, и вообще. Спешу огорчить - это не так.
На сторонних форумах неоднократно проводились тесты быстродействия табулированного и нетабулированного кода - ни по скорости, ни по наличию багов в собранных бинарниках отличия нет. Более того, всем известная директива препроцессора #pragma tabsize позволяет переназначить для интерпретатора размер отступа - ставьте хоть ноль и не табулируйте.
Другое дело, что нетабулированный код на порядок сложнее читать самому, а следовательно - при возникновении необходимости внести изменения создает дополнительные проблемы в понимании алгоритма работы. Впрочем, эту проблему можно решить в один клик с помощью Notepad++ и плагина TextFX.
Ошибка вторая: "Рекурсия - это всегда плохо!"
Не менее часто публикуются сообщения вида "У тебя в коде рекурсия, ее быть не должно".
В таком виде совет, конечно, дикий и неверный. Рекурсивные функции - не есть зло, более того - некоторые вещи, иначе как рекуррентным алгоритмом, не опишешь вообще. К примеру, подсчет факториала проще и быстрее реализовывать рекурсивной функцией, нежели другими способами.
Рекурсия может испортить программисту жизнь только в двух случаях: если программист грамотно не опишет условия выхода из рекурсии или если программист не учитывает, что в какой-то момент может быть переполнен стек. В любом случае, такие косяки - это вина только программиста, отнюдь не ЯП. А это еще хорошо, что в Pawn (из коробки, естественно) нет функций работы с динамической памятью...
О переполнении стека, к слову, мы подробно поговорим в следующем пункте.
Ошибка третья: "Переопределять размер стека - плохо, код с #pragma dynamic - априори кривой!"
Еще одно типичное заблуждение.
Гипотетически может возникнуть ситуация, когда бинарнику необходимо работать с большим количеством переменных, которым в оперативной памяти в любом случае выделено место. И количество этих переменных настолько велико, что дефолтного размера стека в 16384 байта (4096 ячеек) бинарнику просто не хватит, особенно если учесть, что Pawn выделяет под переменную память не байтами, а ячейками (впрочем, об этом более подробно будет сказано далее). Путем нехитрого подсчета получается, что с дефолтным размером стека одновременно можно использовать только 4096 переменных. А если нужно больше?
Именно для этого и существует директива #pragma dynamic, которая позволяет переназначить размер стека при сборке компилятором. Опасность может подстерегать одна - главное, чтобы количество памяти, которое будет требовать бинарник, было свободно на конкретном железе. Впрочем, это уже также проблемы конкретного программиста...
Ошибка четвертая: "Использование bool в нужных случаях экономит ресурсы!"
Вообще, конечно, если смотреть на более серьезные языки программирования - совет действительно правильный. Тип BOOLEAN в них требует значительно меньше ресурсов памяти (аналогично с CHAR - 1 байт) в отличие от целочисленных (INTEGER, от двух байт и выше - для языка C). Во многих ЯП так и происходит...но не в Pawn.
Компилятор Pawn выделяет память не байтами, а ячейками. Одна ячейка равна четырем байтам - а значит, с точки зрения количества используемой памяти между булевой и целочисленной переменной разницы не будет никакой.
Конечно, в случаях, когда могут быть использованы всего два значения - 0 или 1 - грамотнее будет использовать булевы переменные: при написании программ на иных ЯП в этом уже будет польза. Говорить же о том, что использование bool в Pawn позволяет экономить ресурсы - некорректно.
Ошибка пятая: "Всегда пиши/Никогда не пиши stock вместо public"
Существует много мнений о том, что и когда лучше использовать, но аргументы в основном одни и те же: "будет быстрее работать", "меньше жрет памяти" и т.д. Прежде всего хочу сказать одно: функция, как бы Вы ее не описали - stock, public/protected/private и так далее - если содержит одинаковый код, будет занимать одинаковое количество места в памяти, независимо от ее типа.
Различия между stock и public (именно на этих двух типах в Pawn идут основные холивары) в следующем:
- public объявляет функцию публичной, а значит - позволяет ее вызывать вне основного бинарника, stock такого права доступа не дает. Именно поэтому функции, вызываемые по таймеру, или функции, вызываемые из других бинарников через CallRemoteFunction, объявляются только как public;
- для корректного вызова public-функций извне должен быть объявлен их прототип (forward), stock по вышеописанной причине прототипа не требует;
- public-функция будет включена в собранный бинарник, даже если в нем она не используется вообще - опять же, по причине из первого пункта. stock же, если функция в коде не вызывается, в бинарник не будет включена вообще. Правда, смысл держать неиспользуемую функцию в коде мне непонятен.
Есть еще несколько различий, но основные я указал. Что удобнее использовать в каждом конкретном случае - решать только программисту.
Материал будет дополняться, в случае дополнения первый пост будет изменен.
Материал является авторским и без явного согласия автора к публикации на сторонних ресурсах запрещен.
Ошибка первая: "Нетабулированный код работает медленнее и хуже"
Один из наиболее частых советов для "оптимизации" кода - его табулирование. Дескать, нетабулированный код и собирается хуже, и работает медленнее, и вообще. Спешу огорчить - это не так.
На сторонних форумах неоднократно проводились тесты быстродействия табулированного и нетабулированного кода - ни по скорости, ни по наличию багов в собранных бинарниках отличия нет. Более того, всем известная директива препроцессора #pragma tabsize позволяет переназначить для интерпретатора размер отступа - ставьте хоть ноль и не табулируйте.
Другое дело, что нетабулированный код на порядок сложнее читать самому, а следовательно - при возникновении необходимости внести изменения создает дополнительные проблемы в понимании алгоритма работы. Впрочем, эту проблему можно решить в один клик с помощью Notepad++ и плагина TextFX.
Ошибка вторая: "Рекурсия - это всегда плохо!"
Не менее часто публикуются сообщения вида "У тебя в коде рекурсия, ее быть не должно".
В таком виде совет, конечно, дикий и неверный. Рекурсивные функции - не есть зло, более того - некоторые вещи, иначе как рекуррентным алгоритмом, не опишешь вообще. К примеру, подсчет факториала проще и быстрее реализовывать рекурсивной функцией, нежели другими способами.
Рекурсия может испортить программисту жизнь только в двух случаях: если программист грамотно не опишет условия выхода из рекурсии или если программист не учитывает, что в какой-то момент может быть переполнен стек. В любом случае, такие косяки - это вина только программиста, отнюдь не ЯП. А это еще хорошо, что в Pawn (из коробки, естественно) нет функций работы с динамической памятью...
О переполнении стека, к слову, мы подробно поговорим в следующем пункте.
Ошибка третья: "Переопределять размер стека - плохо, код с #pragma dynamic - априори кривой!"
Еще одно типичное заблуждение.
Гипотетически может возникнуть ситуация, когда бинарнику необходимо работать с большим количеством переменных, которым в оперативной памяти в любом случае выделено место. И количество этих переменных настолько велико, что дефолтного размера стека в 16384 байта (4096 ячеек) бинарнику просто не хватит, особенно если учесть, что Pawn выделяет под переменную память не байтами, а ячейками (впрочем, об этом более подробно будет сказано далее). Путем нехитрого подсчета получается, что с дефолтным размером стека одновременно можно использовать только 4096 переменных. А если нужно больше?
Именно для этого и существует директива #pragma dynamic, которая позволяет переназначить размер стека при сборке компилятором. Опасность может подстерегать одна - главное, чтобы количество памяти, которое будет требовать бинарник, было свободно на конкретном железе. Впрочем, это уже также проблемы конкретного программиста...
Ошибка четвертая: "Использование bool в нужных случаях экономит ресурсы!"
Вообще, конечно, если смотреть на более серьезные языки программирования - совет действительно правильный. Тип BOOLEAN в них требует значительно меньше ресурсов памяти (аналогично с CHAR - 1 байт) в отличие от целочисленных (INTEGER, от двух байт и выше - для языка C). Во многих ЯП так и происходит...но не в Pawn.
Компилятор Pawn выделяет память не байтами, а ячейками. Одна ячейка равна четырем байтам - а значит, с точки зрения количества используемой памяти между булевой и целочисленной переменной разницы не будет никакой.
Конечно, в случаях, когда могут быть использованы всего два значения - 0 или 1 - грамотнее будет использовать булевы переменные: при написании программ на иных ЯП в этом уже будет польза. Говорить же о том, что использование bool в Pawn позволяет экономить ресурсы - некорректно.
Ошибка пятая: "Всегда пиши/Никогда не пиши stock вместо public"
Существует много мнений о том, что и когда лучше использовать, но аргументы в основном одни и те же: "будет быстрее работать", "меньше жрет памяти" и т.д. Прежде всего хочу сказать одно: функция, как бы Вы ее не описали - stock, public/protected/private и так далее - если содержит одинаковый код, будет занимать одинаковое количество места в памяти, независимо от ее типа.
Различия между stock и public (именно на этих двух типах в Pawn идут основные холивары) в следующем:
- public объявляет функцию публичной, а значит - позволяет ее вызывать вне основного бинарника, stock такого права доступа не дает. Именно поэтому функции, вызываемые по таймеру, или функции, вызываемые из других бинарников через CallRemoteFunction, объявляются только как public;
- для корректного вызова public-функций извне должен быть объявлен их прототип (forward), stock по вышеописанной причине прототипа не требует;
- public-функция будет включена в собранный бинарник, даже если в нем она не используется вообще - опять же, по причине из первого пункта. stock же, если функция в коде не вызывается, в бинарник не будет включена вообще. Правда, смысл держать неиспользуемую функцию в коде мне непонятен.
Есть еще несколько различий, но основные я указал. Что удобнее использовать в каждом конкретном случае - решать только программисту.
Материал будет дополняться, в случае дополнения первый пост будет изменен.
Материал является авторским и без явного согласия автора к публикации на сторонних ресурсах запрещен.