Pull to refresh

Разбираемся с проблемой мертвого кода и инклудами

Reading time 5 min
Views 1.8K
В этой статье мы поговорим о некоторых иногда упускаемых разработчиками аспектах, влияющих на общую производительность веб приложения. В частности рассмотрим как влияет на производительность множественные подключения внешних файлов, наличие «мертвого» кода, акселерация путем кешеров опкода и FastCGI для PHP.

В какой-то мере статья является следствием топика «Хранение кода в бд или собираем код по кирпичикам», так как именно после него возникли множественные вопросы о производительности (кстати не так давно запустился еще один проект построенный на указанной технологии хранения всего кода в бд). Одним из преимуществ рассмотренного подхода была возможность получения на выходе чистого монолитного кода, не содержащего «мертвых» участков и подключений внешних файлов (например посредством include или require. Далее для простоты называем это просто «инклуды»). Для тех, кто не знаком с тем топиком, поясню, что «мертвым» называется код, который заведомо не будет выполнен на странице.


Немного науки. Вот что рассказывает о мертвом коде директор Института системного проектирования РАН член-корреспондент РАН один авторитетный дядька Виктор Иванников:



«Мертвый код — это такая часть программы или электрической схемы, которая никогда, ни при каких условиях, ни при каких входных данных не выполняется.Это существенная проблема, особенно для систем с ограниченной памятью. Мертвый код может занимать до 30% программы, особенно его много получается при развитии приложений, ведь в них вносятся все новые и новые куски. Проблема мертвого кода алгоритмически неразрешима. Это значит, что я не могу создать такой инструмент, который для любой программы находил бы мертвый код.


Этой проблемой заинтересовались в начале 50-х годов. Соответствующие теорема Райса и теорема Успенского были доказаны одновременно и независимо, только у Владимира Андреевича она формулируется более обобщенно. Теоремы фактически гласят, что распознавание любого нетривиального свойства алгоритма является неразрешимой проблемой.»


От себя добавлю, что в веб программировании количество мертвого кода обычно на порядок больше. При своповом подключении его доля легко превышает порог и в 80-90%. То есть только каждая 20 строка кода будет реально выполнена. Особенно это хорошо видно при применении «тяжелых» фреймворков.



Тут же введем пару определений, которые будем использовать ниже:
монолит — код в котором не используются подключения внешних файлов (инклудов)
чистый монолит — монолит, в котором нет «мертвого» кода.




Как было заявлено, чистый монолитный код значительно увеличивает общую производительность приложения, что вызвало немало споров и подозрений в комментариях. Справедливо возникали вопросы, насколько «мертвый» код и инклуды вообще тормозят приложения? А в качестве решения проблем с производительностью и чистотой кода упоминались кешеры опкода, «магические» функции автоподключения классов и методов (далее для простоты будем называть это просто autoload), а также fastCGI.


Что ж, попробуем поглубже разобраться в поставленном вопросе в поисках истины. Итак, поехали.


А есть ли проблема?


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



Начнем с мертвого кода. Создаем страницы на 2000, 5000, 10000 строк кода и натравливаем на них apacheBenchmark. Получаем следующие результаты:

количество строк запросов/сек среднее время на запрос доля мертвого кода
2000 72.24 0.013 0
5000 32.20 0.031 0.6
10000 15.97 0.062 0.75

Здесь и далее указано время, полученное на стареньком ноутбуке FS Amilo 1718. Результаты контрольных тестов, проведенных на разных системах с разными ОС И ФС пропорционально не отличались



Как видим, время затраченное на обработку скрипта фактически прямо пропорционально количеству строк в нем (что вполне логично, ведь так как мы в скрипте ничего не делаем, то основное время уходит только на его парсинг. Больше строк — соответственно больше время парсинга). Как велико это время? Оно существенно. Согласитесь, даже в идеале при отсутствии мертвого кода 13 тысячных — это много (не говоря уже о 62, как в третьем тесте). За это время можно отдать готовую страницу, выполнив на ней подключение к бд, запросы и прочее. У тут только парсинг…


Теперь разберемся с инклудами. Будем имитировать 4 ситуации:
— «монолит» (без инклудов и мертвого кода)
— «своп» (Имитирует безусловное подключение всех библиотек, которые обычно объединены в большие файлы. Влечет мало инклудов больших файлов. Много мертвого кода)
— «autoload» (Имитирует подключение классов/методов/функций при необходимости (обычно в момент обращения к несуществующему классу/методу/функции). Влечет много инклудов маленьких файлов. Почти нет мертвого кода)
— Пару промежуточных сосотояний между свопом и autoload. Just for fun Для полноты картины :)

Результаты отображены в следующей таблице (в порядке производительности):

количество строк инклудов запросов/сек среднее время на запрос доля мертвого кода тип имитации
2000 0 72.24 0.013 0 чистый монолит
2000 60 35.12 0.028 0 autoload
5000 7 25.63 0.039 0.6
10500 14 13.2 0.076 0.81
15000 5 9.78 0.1 0.87 своп


Как и ожидалось, на первом месте идет монолит, на втором с почти двукратном отставанием autoload. Ну а своп нервно курит в сторонке :) (Правда это только пока, потом он еще надерет задницу автолоаду. Читаем статью дальше). Но время на обработку запроса все еще очень велико…

Кстати, эта таблица как раз подтверждает указанную мной цифру в полученном выигрыше в 600% при переводе системы на чистый монолит.



Есть проблема — есть решение


Основным средством решения проблемы и увеличения производительности приложения за счет сокращения издержек на парсинг страниц являются кешеры байт кода. Самые известные из систем кеширования (в PHP например) это eAccelerator, xCache и APC. Для тестирования был выбран eAccelerator, как самый распространенный и быстрый в условиях нашей задачи. Довольно подробно о сравнении систем кеширования можно почитать здесь. Вот результаты с использованием кеша опкода (в порядке производительности):


количество строк инклудов запросов/сек
(без кеша опкода)
запросов/сек
с кешом опкода
коэффициент акселлерации тип имитации
2000 0 72.24 412.86 5.71 чистый монолит
15000 5 9.78 204.6 20.92 своп
10500 14 13.2 192.2 14.56
2000 60 35.12 112.11 3.19 autoload



И вот тут мы наблюдаем очень интересные факты. При использовании кеша опкода, несмотря на огромное количество мертвого кода своповое подключение оказывается почти в 2 раза эффективнее autoload! И все благодаря безумному коэффициенту акселлерации в 21x. А вот у autoload коэффициент акселлерации оказался самым низким — кешер опкода всего в 3 раза снизил издержки. Отсюда делаем несложный вывод: кеш опкода практически решает проблему «мертвого» кода, но не решает проблему инклудов.Кешеры также не любят большое количество мелких файлов, показывая на них наименьший коэффициент прироста производительности.


А как же FastCGI?


По мнению многих, еще одним способом ускорения скриптов и уменьшения затрат на «подгрузку» скрипта является запуск PHP в режиме FastCGI. Это — не так!. Почему fastCGI не ускоряет PHP вы можете прочитать у Котерова здесь. Кстати там же проведены его тесты eAccelerator на примере Zend Framework. Результаты полученные Дмитрием Котеровом очень близки к приведенным в этом топике.


Заключение


Целью статьи ставилось подробное рассмотрение влияния мертвого кода и подключение внешних файлов в коде на конечную производительность приложения. Как видите влияние этих факторов довольно существенное.

Cтатья написана Napolsky, по известным причинам он не смог ее опубликовать. Все + ему в карму ;)
Tags:
Hubs:
+54
Comments 85
Comments Comments 85

Articles