>백엔드 개발 >PHP7 >PHP 7.4의 사전 로드(Opcache 사전 로드)

PHP 7.4의 사전 로드(Opcache 사전 로드)

藏色散人
藏色散人원래의
2019-11-30 14:04:457327검색

PHP 7.4에는 코드 성능을 크게 향상시킬 수 있는 기능인 사전 로드 지원이 추가되었습니다.

간단히 작동 방식은 다음과 같습니다.

● 파일을 미리 로드하려면 사용자 정의 PHP 스크립트를 작성해야 합니다.

● 이 스크립트는 서버가 시작될 때 한 번 실행됩니다.

● 미리 로드된 모든 파일은 In- 메모리는 모든 요청에 ​​사용할 수 있습니다.

● 미리 로드된 파일에 대한 변경 사항은 서버가 다시 시작될 때까지 영향을 미치지 않습니다.

자세히 살펴보겠습니다.

#Opcache

미리 로드는 opcache 위에 구축되지만 정확히 동일하지는 않습니다. Opcache는 PHP 소스 파일을 가져와 "opcode"로 컴파일한 다음 컴파일된 파일을 디스크에 저장합니다.

opcode는 런타임에 쉽게 해석되는 코드의 하위 수준 표현으로 생각할 수 있습니다. 따라서 opcache는 소스 파일과 PHP 인터프리터가 실제로 런타임에 필요로 하는 것 사이의 변환 단계를 건너뜁니다. 큰 승리입니다!

하지만 우리는 얻을 것이 더 많습니다. Opcached 파일은 다른 파일에 대해 알지 못합니다. 클래스 A가 클래스 B에서 확장되는 경우에도 런타임 시 서로 연결되어야 합니다. 또한 opcache는 소스 파일이 수정되었는지 확인하고 이를 기반으로 캐시를 무효화하는지 확인합니다.

이것이 바로 사전 로드가 작동하는 곳입니다. 사전 로드는 소스 파일을 opcode로 컴파일할 뿐만 아니라 관련 클래스, 특성 및 인터페이스를 함께 연결합니다. 그런 다음 실행 가능한 코드(즉, PHP 인터프리터가 사용할 수 있는 코드)의 "컴파일된" 덩어리를 메모리에 저장합니다.

이제 요청이 서버에 도달하면 오버헤드 없이 이미 메모리에 로드된 코드 베이스의 일부를 사용할 수 있습니다.

그렇다면 "코드베이스의 일부"란 무엇을 의미하나요?

#사전로드 실제

사전로드를 위해서는 개발자가 서버에 로드할 파일을 알려주어야 합니다. 이 작업은 간단한 PHP 스크립트를 사용하여 수행되었으며 실제로 어려운 점은 없었습니다.

규칙은 간단합니다.

● 사전 로드 스크립트를 제공하고 opcache.preload 명령을 사용하여 이를 php.ini 파일에 연결합니다.

● 사전 로드하려는 모든 PHP 파일은 opcache_compile_file()로 전달되거나 사전 로드 스크립트에서 한 번만 전달되어야 합니다.

Laravel과 같은 프레임워크를 미리 로드하고 싶다고 가정해 보세요. 스크립트는 Vendor/laravel 디렉터리의 모든 PHP 파일을 살펴보고 하나씩 추가해야 합니다.

php.ini에서 이 스크립트에 연결하는 방법은 다음과 같습니다.

opcache.preload=/path/to/project/preload.php

이것은 더미 구현입니다.

$files = /* An array of files you want to preload */;
foreach ($files as $file) {
    opcache_compile_file($file);
}

#경고: 연결되지 않은 클래스를 미리 로드할 수 없습니다.

잠깐, 경고가 있습니다! 파일을 미리 로드하려면 해당 종속성(인터페이스, 특성 및 상위 클래스)도 미리 로드해야 합니다.

클래스 종속성에 문제가 있는 경우 서버 시작 시 알림이 표시됩니다.

Can't preload unlinked class 
Illuminate\Database\Query\JoinClause: 
Unknown parent 
Illuminate\Database\Query\Builder

Look, opcache_compile_file()은 파일을 구문 분석하지만 실행하지는 않습니다. 이는 클래스에 미리 로드되지 않은 종속성이 있는 경우 자체적으로 미리 로드할 수 없음을 의미합니다.

이는 치명적인 문제는 아니며 서버는 정상적으로 작동할 것입니다. 하지만 원하는 사전 로드된 파일을 모두 얻을 수는 없습니다.

다행히 링크된 파일도 로드되었는지 확인하는 방법이 있습니다. opcache_compile_file 대신 require_once를 사용하고 등록된 자동 로더(아마 작곡가의 것)가 나머지를 처리하도록 할 수 있습니다.

$files = /* All files in eg. vendor/laravel */;
foreach ($files as $file) {
    require_once($file);
}

주의할 사항이 몇 가지 더 있습니다. 예를 들어 Laravel을 미리 로드하려고 하면 프레임워크의 일부 클래스는 아직 존재하지 않는 다른 클래스에 의존합니다. 예를 들어, 파일 시스템 캐시 클래스 조명 파일 시스템 캐시는 LeagueFlysystemCachedStorageAbstractCache에 의존하며, 파일 시스템 캐시를 사용한 적이 없다면 이를 프로젝트에 설치하지 못할 수도 있습니다.

모든 것을 미리 로드하려고 할 때 "클래스를 찾을 수 없음" 오류가 발생할 수 있습니다. 다행스럽게도 기본 Laravel 설치에는 이러한 클래스가 몇 개만 있으므로 쉽게 무시할 수 있습니다. 편의를 위해 다음과 같이 파일을 더 쉽게 무시할 수 있는 작은 프리로더 클래스를 작성했습니다:

class Preloader
{
    private array $ignores = [];
    private static int $count = 0;
    private array $paths;
    private array $fileMap;
    public function __construct(string ...$paths)
    {
        $this->paths = $paths;
        // We'll use composer's classmap
        // to easily find which classes to autoload,
        // based on their filename
        $classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php';
        $this->fileMap = array_flip($classMap);
    }
    
    public function paths(string ...$paths): Preloader
    {
        $this->paths = array_merge(
            $this->paths,
            $paths
        );
        return $this;
    }
    public function ignore(string ...$names): Preloader
    {
        $this->ignores = array_merge(
            $this->ignores,
            $names
        );
        return $this;
    }
    public function load(): void
    {
        // We'll loop over all registered paths
        // and load them one by one
        foreach ($this->paths as $path) {
            $this->loadPath(rtrim($path, '/'));
        }
        $count = self::$count;
        echo "[Preloader] Preloaded {$count} classes" . PHP_EOL;
    }
    private function loadPath(string $path): void
    {
        // If the current path is a directory,
        // we'll load all files in it 
        if (is_dir($path)) {
            $this->loadDir($path);
            return;
        }
        // Otherwise we'll just load this one file
        $this->loadFile($path);
    }
    private function loadDir(string $path): void
    {
        $handle = opendir($path);
        // We'll loop over all files and directories
        // in the current path,
        // and load them one by one
        while ($file = readdir($handle)) {
            if (in_array($file, ['.', '..'])) {
                continue;
            }
            $this->loadPath("{$path}/{$file}");
        }
        closedir($handle);
    }
    private function loadFile(string $path): void
    {
        // We resolve the classname from composer's autoload mapping
        $class = $this->fileMap[$path] ?? null;
        // And use it to make sure the class shouldn't be ignored
        if ($this->shouldIgnore($class)) {
            return;
        }
        // Finally we require the path,
        // causing all its dependencies to be loaded as well
        require_once($path);
        self::$count++;
        echo "[Preloader] Preloaded `{$class}`" . PHP_EOL;
    }
    private function shouldIgnore(?string $name): bool
    {
        if ($name === null) {
            return true;
        }
        foreach ($this->ignores as $ignore) {
            if (strpos($name, $ignore) === 0) {
                return true;
            }
        }
        return false;
    }
}

이 클래스를 동일한 프리로드 스크립트에 추가하면 이제 다음과 같이 전체 Laravel 프레임워크를 로드할 수 있습니다.

// …
(new Preloader())
    ->paths(__DIR__ . '/vendor/laravel')
    ->ignore(
        \Illuminate\Filesystem\Cache::class,
        \Illuminate\Log\LogManager::class,
        \Illuminate\Http\Testing\File::class,
        \Illuminate\Http\UploadedFile::class,
        \Illuminate\Support\Carbon::class,
    )
    ->load();

#이 작동합니까?

물론 가장 중요한 질문입니다. 모든 파일이 올바르게 로드되었습니까? 서버를 다시 시작한 다음 opcache_get_status()의 출력을 PHP 스크립트에 덤프하여 간단히 테스트할 수 있습니다. 여기에는 사전 로드된 파일이 소비하는 메모리와 함께 사전 로드된 모든 함수, 클래스 및 스크립트가 나열되는 preload_statistics라는 키가 있는 것을 볼 수 있습니다.

# Composer 지원

유망한 기능은 이미 대부분의 최신 PHP 프로젝트에서 사용되는 작곡가 기반 자동 사전 로드 솔루션일 수 있습니다. 사람들은 사전 로드 파일을 생성하는 사전 로드 구성 옵션을 Composer.json에 추가하기 위해 노력하고 있습니다. 현재 이 기능은 아직 개발 중이지만 여기에서 따라갈 수 있습니다.

#서버 요구 사항

사전 로드를 사용할 때 DevOps와 관련하여 언급해야 할 중요한 두 가지 사항이 더 있습니다.

미리 로드하려면 php.ini에 항목을 지정해야 한다는 것을 이미 알고 계셨습니다. 즉, 공유 호스팅을 사용하면 PHP를 자유롭게 구성할 수 없습니다. 실제로 개별 프로젝트에 대해 사전 로드된 파일을 최적화하려면 전용(가상) 서버가 필요합니다. 이것을 기억하십시오.

또한 메모리 파일을 다시 로드해야 할 때마다 서버를 다시 시작해야 한다는 점을 기억하세요(php-fpm을 사용하는 경우 충분합니다). 이는 대부분의 사람들에게 당연한 것처럼 보일 수 있지만 여전히 언급할 가치가 있습니다.

#Performance

이제 가장 중요한 질문입니다. 사전 로드가 실제로 성능을 향상합니까?

답은 '예'입니다. Ben Morel은 이전에 링크된 동일한 작곡가 질문에서 찾을 수 있는 몇 가지 벤치마크를 공유했습니다.

흥미롭게도 코드 베이스에서 자주 사용되는 클래스인 "핫 클래스"만 미리 로드하도록 결정할 수 있습니다. Ben의 벤치마크에서는 약 100개의 인기 클래스만 로드하는 것이 실제로 모든 클래스를 사전 로드하는 것보다 더 나은 성능 향상을 가져온다는 것을 보여줍니다. 이는 13%와 17% 성능 향상의 차이입니다.

물론 어떤 클래스를 미리 로드해야 하는지는 특정 프로젝트에 따라 다릅니다. 처음에는 최대한 미리 로드하는 것이 현명합니다. 약간의 비율 증가가 필요한 경우 런타임에 코드를 모니터링해야 합니다.

물론 이 모든 작업은 자동화될 수 있으며 아마도 미래에도 자동화될 것입니다.

이제 기억해야 할 가장 중요한 점은 사용자가 사전 로드 파일을 직접 만들 필요가 없도록 작곡가가 지원을 추가한다는 점이며, 사용자가 모든 권한을 갖고 있는 한 서버에서 이 기능을 쉽게 설정할 수 있다는 것입니다. 그 위에.

번역: https://stitcher.io/blog/preloading-in-php-74

위 내용은 PHP 7.4의 사전 로드(Opcache 사전 로드)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.