shape1
shape2
shape3
shape4
shape7
shape8

Типичные ошибки "оптимизаторов": разбор и обоснование.


SHOROOP

Освоившийся
Пользователь
30.01.2014
58
56
0
32
Скриптер
В связи с некоторыми событиями будет лучше, если появится тема, в которой я разберу несколько типичных ошибок горе-оптимизаторов, вводящих остальных в заблуждение.

Ошибка первая: "Нетабулированный код работает медленнее и хуже"

Один из наиболее частых советов для "оптимизации" кода - его табулирование. Дескать, нетабулированный код и собирается хуже, и работает медленнее, и вообще. Спешу огорчить - это не так.

На сторонних форумах неоднократно проводились тесты быстродействия табулированного и нетабулированного кода - ни по скорости, ни по наличию багов в собранных бинарниках отличия нет. Более того, всем известная директива препроцессора #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 же, если функция в коде не вызывается, в бинарник не будет включена вообще. Правда, смысл держать неиспользуемую функцию в коде мне непонятен.

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

Материал будет дополняться, в случае дополнения первый пост будет изменен.

Материал является авторским и без явного согласия автора к публикации на сторонних ресурсах запрещен.

 

Maxim

Освоившийся
Пользователь
24.01.2014
60
29
0
27
Наконец-то, кто-то соизволил объяснить.. спасибо.

 

Smoul

Новичок
Пользователь
06.09.2016
19
0
0
А какая разница между динамическими системами и нединамическими?