PHP жұмысының жылдамдығын арттыру
OPCache және JIT. Егер біз базалық CRUD-тан күрделі нәрсе жазсақ, бұл бізге қажет.
Кіріспе
Белгілі болғандай, бағдарламалау тілдерінің екі негізгі түрі бар: компиляцияланатын және интерпретацияланатын. Олардың атауларынан түсінікті болғандай, компиляцияланатын тілдердің коды орындалмас бұрын процессор тікелей оқитын бинарлық кодқа компиляциялануы керек. Ал интерпретацияланатын тілдердің коды оқылып, операциялық кодқа (бұдан әрі — опкод) аударылады, ол өз кезегінде бинарлық кодқа аударылып, процессорға орындалуға беріледі.
Интерпретатор алдымен орындалуға берілген файлды оқып, оның кодын интерпретациялап, тәуелділіктерді жүктеп, опкодтарға аударып, содан кейін опкодтарды Zend виртуалды машинасы өңдеп, тек содан кейін процессорға орындалуға беретіндіктен, интерпретацияланатын тілдер осы артық операциялар есебінен баяу деп саналады.
Бұл жағдай белгілі бір уақытқа дейін осылай болды, адамдар өндірістік серверлерде қолданбалардың бастапқы коды өте сирек өзгеретінін және әр жолы бірдей файлдарды оқып, өңдеу уақытты босқа кетіру екенін түсінгенге дейін. Бір рет оқылған файлды жадта компиляцияланған түрде сақтап, әр сұраныс сайын қайта компиляцияламай-ақ қоюға болады.
Осылайша OPCache кеңейтімі пайда болды.
PHP-де скрипттің өмірлік циклі
PHP-де скрипт шақырылған сәттен бастап орындалу қадамдары келесідей көрінеді: Парсинг > Компиляция > Орындалу.
Бірақ егер біз OPCache кеңейтімін қосатын болсақ, онда скрипттің өмірлік цикліне компиляция мен орындалу арасында тағы екі қадам қосылады: Оптимизация және Кэштеу.
Осылайша, OPCache ескерілгенде, PHP-де скрипттің өмірлік циклі келесідей көрінеді: Парсинг > Компиляция > Оптимизация > Кэштеу > Орындалу
Парсинг
Бұл скрипттің өмірлік цикліндегі бірінші қадам. Бұл кезеңде парсер мен лексер әрекет етеді, олар кодты токендерге бөледі және компиляторға береді. Бұл процесс токенизация деп аталады және компиляторға токендердің өзара байланысын түсінуге көмектеседі.
Компиляция
Бұл қадамда алынған токендер опкодтарға айналады, онда қолданбаны орындауға арналған нұсқаулар бар.
Оптимизация
PHP ерекшеліктеріне байланысты файлдар арасындағы оптимизация жүргізу мүмкіндігі жоқ. Осылайша, оптимизация тек бір файл деңгейінде ғана жүзеге асады.
Файл ішіндегі оптимизация алдын ала белгілі мәндерді оңтайландыра алады. Мысалы, бізде script.php атты файл бар және ол /tmp директориясында орналасқан. Келесі скрипт оңтайландырылады:
if (__DIR__ == '/tmp') {
echo 'yes';
} else {
echo 'no';
}
OPCache файлдың орналасқан жерін алдын ала білетіндіктен, оны код ішінде қайта тексерудің қажеті жоқ және компилятор бұл жазбаны қарапайым түрде қысқартады
// Если на этапе компиляции файл находился в директории /tmp
echo 'yes';
// В иных случаях
echo 'no';
Тағы бірнеше мысалдар:
if ($a) {
echo 'foo';
} else {
echo 'bar';
}
Мұнда компилятор $a айнымалысында қандай мән болатынын алдын ала болжай алмайды және оны орындау кезінде тексеруді қалдырады
if function_exists('my_custom_function')) { }
Бұл жағдайда компилятор коды оңтайландыра алмайды, өйткені my_custom_function функциясы тілдің стандартты атау кеңістігіне кірмейді. Компилятор пайдаланушы функцияларының бар-жоғын тексеруді оңтайландырмайды, өйткені олар басқа файлдарда болуы мүмкін.
if (function_exists('array_merge')) {
echo 'yes';
}
Бірақ стандартты функциялардың бар-жоғын тексеруді компилятор оңтайландырады, өйткені оның бар-жоғын стандартты атау кеңістігінде тексеруге болады және осы кодтың орындалу нәтижесі алдын ала белгілі.
Константтармен де жағдай осындай. Константтар ешқашан өзгермейтіндіктен, компилятор оларды жадтағы көрсеткіш арқылы шақырудың орнына, олардың мәнін кодтың қажетті жерлеріне тікелей қояды.
Осылайша, келесі код
const SOME_STRING = 'foo';
echo SOME_STRING;
Мәнді тікелей шығару түріне айналады:
echo 'foo';
Кэштеу
Бұл қадамда OPCache скрипттерді сақтау үшін жалпы жад бөледі. Бұл сегмент жад тұрақты негізде бөлінетінін, яғни өзгертілмейтінін немесе жадтан жойылмайтынын түсіну маңызды.
Бұл жад сегментінің өлшемі opcache.memory_consumption параметрінде көрсетіледі. Әр түрлі қолданбалардың әр түрлі талаптары болғандықтан, бөлінген жадтың өлшемін баптауға арналған әмбебап ұсыныс жоқ. Бірақ қолданбаның көлемі неғұрлым үлкен болса, соғұрлым оған көбірек жад бөлу керек, бұл қисынды. Фреймворк файлдары да компиляцияланып, жалпы жадқа орналастырылатындықтан, жалпы сегментке мүмкіндігінше көбірек жад бөлген дұрыс.
Бұл үлкен жобалар туралы айтқанда. Ал егер шағын жобалар туралы айтсақ, сіз оқып отырған осы блогқа 128MB жеткілікті, оның тек шамамен 50MB ғана қолданылады.
Сонымен, OPCache бұл жадқа не жазады? Алдымен, өзгермейтін деректер жалпы жадқа жазылады. Бұл компиляцияланған кластар, сондай-ақ функциялар мен константтар болуы мүмкін. Осылайша, жадта барлық қажетті деректер сақталады және оларға қол жеткізу көрсеткіш арқылы жүзеге асады, қайта компиляциялаусыз немесе объектілерді көшірусіз.
Яғни, OPCache скриптті жүктеп, оны компиляциялап, компиляцияланған деректерді сақтау үшін қажетті жадтың нақты өлшемін есептеп, оларды сол жерге орналастырады. Осылайша, жад ешқашан босатылмайды және фрагменттелмейді, бір рет орналастырылған объект жадта өзінің мекенжайы бойынша әрқашан қолжетімді болып қалады. Бөлінетін жад мөлшерін баптауға оралсақ, қажетті жад — бұл код файлының өлшемі емес, ол жасайтын объектілер мен айнымалылар саны екенін ұмытпаңыз.
JIT компиляциясы
Тоқтаңыз! Бірақ жоғарыда компиляция туралы айтылған еді, неге тағы да ол туралы айту керек?
Иә және жоқ. JIT компиляциясы файлдарды опкодтарға компиляциялаудан төмен тұрады, өйткені ол жоғарыда компиляцияланған опкодтарды тікелей машиналық кодқа, процессорға арналған нұсқауларға аударады, PHP-нің виртуалды машинасын айналып өтеді.
Әрбір файл немесе тіпті әрбір код блогы JIT көмегімен компиляцияланбайды. Бұл компиляция түрі кодтың логикалық бөлігін айтарлықтай жылдамдата алады. Бұл массивтермен жұмыс істеуді, байттармен манипуляцияларды, суреттерді өңдеуді жылдамдатуға мүмкіндік береді және тіпті PHP-де Machine Learning әзірлеуге қол жеткізеді! Бірақ JIT компиляциясы енгізу-шығару функцияларын жылдамдатпайды, өйткені дискілер мен желінің жылдамдығының өз шектеулері бар.
JIT компиляциясын, кез келген басқа құрал сияқты, оның қажеттілігін түсінген кезде қолдану керек. Ол орташа блогты жылдамдатпайды, өйткені блогтың жұмысы негізінен CRUD операциялары.
Қорытынды
Сонымен, бізге OPCache және JIT қажет пе? Әрине, қажет! Егер қолданба енгізу-шығару операцияларына тәуелді болмаса, мысалы, күрделі есептеулер үшін пайдаланылса, компиляцияланған код интерпретацияланған кодқа қарағанда әлдеқайда жылдам жұмыс істейді.
Жеке тәжірибемнен айта аламын, JIT қосылғаннан кейін қиылысатын аймақтарды іздеу айтарлықтай жылдам жұмыс істеді, өйткені бұл таза логикалық деректерді өңдеу.
Бірақ, әрине, ешбір компилятор бастапқыда нашар жазылған кодты жылдамдата алмайды. Сондықтан оптимизаторға сеніңіз, бірақ өзіңіз де қателеспеңіз!