Вся наша жизнь это борьба с тормозами и нехваткой времени. Каждый день мы тратим по несколько часов на оптимизацию. Каждый из нас старается оптимизировать все, что попадает под руку. А ты уверен, что ты это делаешь правильно? Может быть, есть возможность что-то сделать еще лучше?
А может легче обновить комп?
Я понимаю, что все сейчас зажрались и выполняют свои обязанности лениво и не принужденно. Лично я до такой степени привык, что за меня все делает железный друг, что даже забыл, как выглядит шариковая ручка. Недавно мне пришлось писать заявление на отпуск на простой бумаге, так я забыл, как пишется буква "ю". Пришлось подглядывать, как она выглядит на клавиатуре :).
Даже для того, чтобы написать текст из двух строк, мы включаем свой компьютер и загружаем MS Word, тратя на это драгоценное время. А может легче было бы написать этот текст вручную? Я тебя понимаю - не солидно!!!
Программеры - так это вообще полное бесстыдство, тра-та-та (дальше все вырезано цензурой). Если они считают, что раз их творение (в виде исходника) никто не увидит, то можно писать что угодно? Так это они ошибаются. С этой точки зрения проги с открытым исходником в большом преимуществе, потому что они намного чище и быстрей.
Создавая код, мы ленимся его оптимизировать не только с точки зрения размера, но и с точки зрения скорости. Глядя на такие творения, хочется выругаться матом, так ведь опять цензура обрежет.
Хакеры далеко не ушли. Если раньше, глядя на кодера или хакера создавался образ прокуренного, заросшего и немытого молодого человека, то сейчас это цифровое существо, залитое балтикой по самые уши за которого все выполняют машины. Тебе медсестра в поликлинике не говорила, что у тебя вместо мочи одно только пиво льется? Не, я ничего против пива не имею, я и сам его люблю. Лучше выпить литр пива, чем выкурить одну сигарету (а сам уже 12 лет бросаю курить и никак не могу).
Все это деградация по методу MS!!! Мы берем в руки мышку и начинаем тыкать ей где попало, забывая про клаву и горячие клавиши. Я считаю, что надо бороться с этим. В последнее время меня самого в послеобеденное время посещает такая лень, что я убираю клаву, запускаю экранную клавиатуру и начинаю работать только крысой. Осталось только покрыть мое тело шерстью и посадить в клетку к таким же ленивым шимпанзе.
Совсем недавно SINtez очень хорошо сказал - не надо тратить большие деньги на апгрейд компа. Начните лучше апгрейд с себя. Давайте, оптимизируем свою работу и то, что мы делаем.
Даю установку
В своем зародыше, эта статья задумывалась как рассказ об оптимизации кода программ. Но когда я уже сел за свою печатную машинку, заправил в нее лоток бумаги и размял пальцы, то понял, что статья должна рассказать тебе намного больше. Ведь я буду говорить про теорию оптимизации, а ее законы действуют везде. По тем же законам ты можешь оптимизировать свой распорядок дня, чтобы успевать все сделать, и свою ОС, чтобы она работала быстрей. Но основа все же будет относиться к коду программ.
Как всегда я постараюсь давать как можно больше реальных примеров, чтобы ты смог убедится в том, что тебе не вешают очередную лапшу на уши, и ты мог применить все сказанное на практике.
Начну я с законов, которые работают не только в кодинге, но и в реальной жизни. Ну а напоследок я оставлю только то, что может пригодиться только при оптимизации кода. Но даже если ты никаким местом не относишься к кодингу, я советую тебе прочитать статью полностью. Никогда нельзя знать, что тебе может пригодиться завтра.
Законы оптимизации
ЗАКОН №1: Оптимизировать можно все. Даже там, где тебе кажется, что все и так работает быстро, можно сделать еще быстрее.
Это действительно так. И этот закон очень сильно проявляется в кодинге. Идеального кода не существует. Даже простую операцию сложения 2+2, тоже можно оптимизировать. Чтобы достичь максимального результата, нужно действовать последовательно и желательно в том порядке, в котором я буду описывать.
Ищи слабые места
ЗАКОН №2: Первое с чего нужно начинать - это с поиска самых слабых и тормознутых мест. Зачем начинать оптимизацию с того, что и так работает достаточно быстро. Если ты будешь оптимизировать сильные места, то можешь нарваться на неожиданные конфликты.
Тут же я вспоминаю пример из своей собственной жизни. Где-то в 1995-96-м году меня посетила одна невероятная идея - написать собственную игру в стиле Doom. Я не собирался ее делать коммерческой, а хотел только потренировать свои мозги на сообразительность. Четыре месяца невероятного труда, и нечто похожее на движок уже было готово. Я создал один голый уровень, по которому можно было перемещаться, и с чувством гордости побежал по коридорам.
Никаких монстров, дверей и атрибутики на нем не было, а тормоза ощущались достаточно значительные. Тут я представил себе, что будет, если добавить монстров и атрибуты, да еще и наделить все это AI.... Вот тут чувство достоинство отвисло от "чуть ниже пояса" по самые колени :). Кому нужен движок, который при разрешении 320х200 (тогда это было круто) в голом виде тормозит со страшной силой? Вот именно....
Понятное дело, что мой виртуальный мир нужно было оптимизировать. Целый месяц я бился над кодом и вылизывал каждый оператор моего движка. Результат - мир стал прорисовываться на 10% быстрей, но тормоза не исчезли. И тут я увидел самое слабое место - вывод на экран. Мой движок просчитывал сцены достаточно быстро, а пробоиной тормозов был именно вывод изображения. Тогда еще не было шины AGP и я использовал простую PCI видюху от S3 с 1 мегом памяти. Пару часов колдовства, и я выжал из PCI все возможное. Откомпилировав движок, я снова загрузился в свой виртуальный мир. Одно нажатие клавиши вперед и я очутился у противоположной стены. Никаких тормозов, сумасшедшая скорость просчета и моментальный вывод на экран.
Как видишь, моя ошибка была в том, что я неправильно определил слабое место своего движка. Я месяц потратил месяц на оптимизацию математики и что в результате? Мизерные 10% прироста в производительности. Но когда я реально нашел слабое звено, то смог повысить производительность в несколько раз.
Слабые места компа
Меня поражают люди, которые гонятся за мегагерцами процессора и сидят на доисторической видюхе от S3, винте на 5400 оборотов и с 32 мегами памяти. Посмотри в корпус своего железного друга и оцени его содержимое. Если ты увидел, что памяти у тебя не более 32 мегов, то встань и громко произнеси: "Уважаемый DIMM, команда выбрала вас. Вы сегодня самое слабое звено и должны покинуть мой компьютер" :). После этого, закупаешь себе 128, а лучше 256 мегов памяти и наслаждаешься ускорением работы Delphi, Photoshop и других тяжелых прог.
В данном случае, наращивание мегагерцов у проца даст более маленький прирост в скорости. Если ты используешь тяжелые приложения при нехватке памяти, то проц начинает тратить слишком много времени на загрузку и выгрузку данных. Ну а если в твоем железном друге достаточно оперативки, то проц уже занимается только расчетами и не расходуется по лишним загрузкам-выгрузкам.
То же самое с видюхой. Если она у тебя слабенькая, то проц будет просчитывать сцены быстрей, чем они будут выводиться на экран. А это грозит простоями и минимальным приростом производительности.
ЗАКОН №3: Следующим шагом ты должен разобрать все операции по косточкам и выяснить, где происходят регулярно повторяющиеся операции. Начинать оптимизацию нужно именно с них.
Опять начнем рассмотрение этого закона с кодинга. Допустим, что у тебя есть следующий код (я приведу просто логику, а не реальную прогу):
1. А:=А*2;
2. Б:=1;
3. Х:=Х+Б;
4. Б:=Б+1;
5. Если Б<100 то перейти на шаг 3;
Любой программист скажет, что здесь слабым местом является первая строка, потому что там используется умножение. Это действительно так. Умножение всегда выполняется дольше, и если заменить его на сложение (А:=А+А) или еще лучше на сдвиг, то ты выиграешь пару тактов процессорного времени. Но это только пару тактов и для процессора это будет незаметно.
Теперь посмотри еще раз на наш код. Больше ничего не видишь? А я вижу. В этом коде используется цикл: "Пока Б<100 будет выполнятся операция Х:=Х+Б". Это значит, что процессору придется выполнить 100 переходов с шага №5 на шаг №3. А это уже не мало. Как же можно здесь что-то оптимизировать? Очень легко. Здесь у нас внутри цикла выполняется две строки 3 и 4. А что если мы внутри цикла размножим их 2 раза:
2. Б:=1;
3. Х:=Х+Б;
4. Б:=Б+1;
5. Х:=Х+Б;
6. Б:=Б+1;
7. Если Б<50 то перейти на шаг 3;
Здесь я разложил цикл на более маленький. Вторую и третью операцию я повторил два раза. Это значит, что за один проход цикла я выполню два раза строки 2 и 3 и только после этого перейду на строку 1, чтобы повторить операцию. Такой цикл уже нужно повторить только 50 раз (потому что за один раз выполняется два действия). Это значит, что я сэкономил 50 операций переходов. Нехило? А это уже несколько сотен тактов процессорного времени.
А что если внутри цикла написать строки 2 и 3 десять раз. Это значит, что за один проход цикла строки 2 и 3 будут вычисляться 10 раз и мне понадобится повторить такой цикл только 10 раз, чтобы получить в результате 100. А это уже экономия 90 операций переходов.
Недостаток этого подхода - увеличился код моей программы, зато повысилась скорость и очень значительно. Этот подход очень хорош, но им не стоит злоупотреблять. С одной стороны увеличивается скорость, а с другой увеличивается размер. А большой размер это враг любой программы.
Повсторяющиеся операции в жизни
Ну, тут примеров туева хуча. Любую циклическую операцию можно оптимизировать. Хочешь пример? Пожалуйста. Допустим у твоего прова есть несколько телефонов доступа. Ты каждый день перезваниваешь на каждый из них в ожидании найти свободный. Ушастый тут же скажет, что пров обязан оптимизировать свои пулы модемов в один, чтобы не надо было трезвонить по всем номерам сразу. Но продвинутый должен знать, что не у каждого хорошая связь с любой станцией города. Поэтому провы держат пулы на разных станциях, чтобы ты мог выбрать тот с которым у тебя лучший коннект. Тогда что же оптимизировать? Очень просто - поставь прогу дозвонщик (таких сейчас полно в инете и мы о них писали) которая сама будет перебирать номера телефонов.
А теперь другой пример - тебе досталась карточка на 1 час какого-то нового прова. Заносить ее в прогу-дозвон не имеет смысла, потому что ты можешь больше никогда не позвонить этому прову. Из-за этой одноразовой операции тебе придется перенастраивать свой дозвонщик на нового прова и потом обратно, а выигрыш практически нулевой, потому что пока ты меняешь настройки уже можно было дозвониться.
Одноразовые операции
ЗАКОН №4. (этот закон как бы расширение предыдущего). Оптимизировать одноразовые операции - это только потеря времени. Сто раз подумай, прежде чем начать мучится с редкими операциями.
Пол годика назад я прочитал рассказ в интернете "Записки жены программиста" (http://www.exler.ru/novels/wife.htm). Очень даже некислый и жизненный рассказ. Когда я его читал, у меня было ощущение, что его написала моя жена :). Слава "Красной Шапочке", что она на такую подлость не способна. Так вот там была такая ситуация:
"Очаровашка выходит замуж за программера и им надо разослать приглашения на свадьбу. Вместо того, чтобы набрать их на печатной машинке, программер кричит, что он крутой и пишет специальную прогу. Написание проги заняло один день и столько же ее отладка".
Главная ошибка - неправильная оптимизация своего труда. Легче набрать шаблон в любом текстовом редакторе и потом только менять фамилии приглашенных на этот траурный день (это я сужу по себе :)). Но даже если нет текстового редактора, писать прогу действительно нет смысла. Затраты большие а пользоваться ей будешь только один раз. Здесь действительно легче будет даже набрать на печатной машинке.
Получается, что одноразовые операции оптимизировать нет смысла. Затраты тут себя не окупают, поэтому не стоит тратить свои нервы на этот бессмысленный труд.
В самом начале статьи я раскритиковал тебя как лентяя, который ленится что-то делать. Так вот именно здесь ты можешь проявлять все свои ленивые качества в полном объеме. В данном случае крутым считается не тот, кто целый день промучился и ничего не добился, а тот, кто выполнил свою работу наиболее эффективно. И эти две вещи путать нельзя.
ЗАКОН №5: Нужно знать внутренности компьютера и принципы его работы. Чем лучше ты знаешь, каким образом компьютер будет выполнять твой код, тем лучше ты сможешь его оптимизировать.
Это последний закон, который я хотел бы тебе сказать, и относится он только к кодингу. Тут трудно привести полный набор готовых решений, но некоторые приемы я постараюсь описать.
1. Старайся поменьше использовать вычисления с плавающей запятой. Любые операции с целыми числами выполняются в несколько раз быстрее.
2. Операции умножения и тем более деления так же выполняются достаточно долго. Если тебе нужно умножить како-то число на 3, то для проца будет легче три раза сложить одно и то же число, выполнить умножение.
А как же тогда экономить на делении? Вот тут нужно знать математику. У проца есть такая вещь как сдвиг. Ты должен знать, что процессор думает с помощью нулей и единиц. Это значит, что числа в нем хранятся в двоичной системе. Например, число 198 для процессора будет выглядеть как 11000110. Теперь посмотрим, как работают операции сдвига.
Сдвиг вправо: Если сдвинуть число 11000110 вправо на одну позицию, то последняя цифра исчезнет и останется только 1100011. Теперь загони это число в калькулятор и переведи его в десятичную систему. Твой результат должен быть 99. Как видишь - это ровно половина числа 198. Вывод - когда ты сдвигаешь число вправо на одну позицию, то ты делишь его на 2.
Сдвиг влево: Возьмем то же самое число 11000110. Если сдвинуть его влево на одну позицию, то с правой стороны освободится место, которое сразу заполняется нулем 110001100. Теперь переведи это число в десятичную систему. У тебя должно получится 396. Ни что не напоминает тебе? Это 198 умноженное на 2.
Вывод: Когда ты сдвигаешь число вправо, то ты делишь его на 2. Когда сдвигаешь влево, то умножаешь его на 2. Так что используй это свойство сдвигов везде, где это возможно, потому что сдвиги работают в десятки раз быстрее.
3. Когда создаешь процедуры, то не пичкай их большим количеством входных параметров. Перед каждым вызовом процедуры ее параметры подымаются в специальную область памяти (стек), а после входа изымаются оттуда. Чем больше параметров, тем больше расходы на общение со стеком.
Тут же нужно сказать, что ты должен быть аккуратным и с самими параметрами. Не вздумай пересылать процедурам переменные, которые могут содержать данные большого объема в чистом виде. Лучше передай адрес ячейки памяти, где хранятся данные, а внутри процедуры работай с этим адресом. Ты представь себе ситуацию, когда тебе нужно передать текст размером одного тома "Войны и мир".... Перед входом в процедуру, программа попытается вогнать все это в стек. Если ты не схватишь его переполнение, то тормоза ощутишь значительные.
4. В самых критичных моментах (как, например вывод на экран) можно воспользоваться языком Assembler. Даже встроенный в Delphi или C++ ассемблер на много быстрее. Ну а если скорость в каком-то месте уж слишком критична, то код ассемблера можно вынести в отдельный модуль. Там его нужно откомпилировать с помощью компиляторов TASM или MASM, и подключить к своей проге.
Ассемблер достаточно быстрая и компактная вещь, но писать достаточно большой проект только на нем это чистый геморрой. Поэтому я не советую им увлекаться, и используй его только в самых критичных для скорости местах.
COMPLETE
Если ты прочитал все внимательно, то можешь считать, что с основами оптимизации ты уже знаком. Но это только основы и тут есть куда развиваться. Я бы мог рассказать больше, но не вижу особого смысла, потому что оптимизация - это процесс творческий и в каждой отдельной ситуации можно подойти с разных сторон. И все же, те законы, которые я сегодня описал, действуют в 99,9% случаев.
Если ты хочешь познать теорию оптимизации более глубоко, то тебе нужно больше изучать принципы работы процессора и операционных систем. Главное, что законы ты уже знаешь, а остальное прейдет со временем и навыками.