복붙노트

require_once를 사용하는 것이 왜 그렇게 나쁜가요?

PHP

require_once를 사용하는 것이 왜 그렇게 나쁜가요?

더 나은 PHP 코딩 방법에 대해 읽은 모든 것은 속도 때문에 require_once를 사용하지 말라는 것을 계속 말합니다.

왜 이런거야?

require_once와 똑같은 일을하는 적절한 / 더 좋은 방법은 무엇입니까? 중요한 경우 PHP5를 사용하고 있습니다.

해결법

  1. ==============================

    1.require_once와 include_once는 시스템이 이미 포함 / 필수 항목에 대한 로그를 유지하도록 요구합니다. 모든 * _once 호출은 해당 로그를 확인하는 것을 의미합니다. 따라서 거기에서 수행되는 추가 작업이 있지만 앱 전체의 속도를 저하시킬만큼 충분합니까?

    require_once와 include_once는 시스템이 이미 포함 / 필수 항목에 대한 로그를 유지하도록 요구합니다. 모든 * _once 호출은 해당 로그를 확인하는 것을 의미합니다. 따라서 거기에서 수행되는 추가 작업이 있지만 앱 전체의 속도를 저하시킬만큼 충분합니까?

    ... 나는 정말로 그것을 의심합니다 ... 당신이 정말로 오래된 하드웨어를 사용하거나 많이하지 않는 한 아닙니다.

    * _once의 수천을하고 있다면, 당신은 더 가벼운 방법으로 직접 작업을 할 수 있습니다. 간단한 앱의 경우 한 번만 포함했는지 확인하는 것만으로 충분하지만 오류를 다시 정의하는 경우 다음과 같이 할 수 있습니다.

    if (!defined('MyIncludeName')) {
        require('MyIncludeName');
        define('MyIncludeName', 1);
    }
    

    나는 개인적으로 * _once 문장을 고수 하겠지만 어리석은 백만 번 통과 벤치 마크에서 두 가지의 차이를 볼 수 있습니다.

                    php                  hhvm
    if defined      0.18587779998779     0.046600103378296
    require_once    1.2219581604004      3.2908599376678
    

    require_once가 10-100 배 느리고 hhvm에서는 require_once가 느리게 느려지는 것이 궁금합니다. 다시 말하지만, 이것은 수천 번 * _once를 실행하는 경우에만 코드와 관련이 있습니다.

    <?php // test.php
    
    $LIMIT = 1000000;
    
    $start = microtime(true);
    
    for ($i=0; $i<$LIMIT; $i++)
        if (!defined('include.php')) {
            require('include.php');
            define('include.php', 1);
        }
    
    $mid = microtime(true);
    
    for ($i=0; $i<$LIMIT; $i++)
        require_once('include.php');
    
    $end = microtime(true);
    
    printf("if defined\t%s\nrequire_once\t%s\n", $mid-$start, $end-$mid);
    
    <?php // include.php
    
    // do nothing.
    
  2. ==============================

    2.이 스레드는 이미 "솔루션 게시"가 있었기 때문에 나를 싫어하게 만듭니다. 모든 의도와 목적으로 잘못되었습니다. 열거하자 :

    이 스레드는 이미 "솔루션 게시"가 있었기 때문에 나를 싫어하게 만듭니다. 모든 의도와 목적으로 잘못되었습니다. 열거하자 :

    그래서 잠시 동안 많은 사람들이 그들의 포함을 위해 class_exists () 메소드를 사용했습니다. 그것은 추한 것 때문에 맘에 들지 않지만 좋은 이유가 있습니다 : require_once ()는 최신 버전의 PHP보다 먼저 비효율적이었습니다. 그러나 그것은 고쳐졌고, 조건부로 컴파일해야하는 여분의 바이트 코드와 추가 메서드 호출이 내부 해시 테이블 검사를 훨씬 능가 할 것이라는 것이 나의 주장이다.

    이제, 입장료 : 테스트 시간이 거의 필요치 않기 때문에 테스트하기가 어렵습니다.

    인터프리터가 구문 분석 모드로 전환 할 때마다 opcode를 생성 한 다음 다시 돌아와야하기 때문에 일반적으로 PHP에서는 비용이 많이 든다. 100 개 이상의 포함을 사용하면 실적에 큰 영향을 미칩니다. require_once를 사용하거나 사용하지 않는 것이 중요한 질문 인 이유는 opcode 캐시가 어려워지기 때문입니다. 이것에 대한 설명은 여기에서 찾을 수 있습니다.하지만이 점은 다음과 같습니다.

  3. ==============================

    3.나는 호기심을 가지고 Tech Your Universe에 대한 Adam Backstrom의 링크를 확인했다. 이 기사에서는 require_once 대신 require를 사용해야하는 이유 중 하나에 대해 설명합니다. 그러나 그들의 주장은 나의 분석을 고수하지 못했다. 나는 해결책을 misanalysed 할지도 모른 지보고 싶을 것이다. 비교를 위해 PHP 5.2.0을 사용했습니다.

    나는 호기심을 가지고 Tech Your Universe에 대한 Adam Backstrom의 링크를 확인했다. 이 기사에서는 require_once 대신 require를 사용해야하는 이유 중 하나에 대해 설명합니다. 그러나 그들의 주장은 나의 분석을 고수하지 못했다. 나는 해결책을 misanalysed 할지도 모른 지보고 싶을 것이다. 비교를 위해 PHP 5.2.0을 사용했습니다.

    require_once를 사용하여 다른 헤더 파일을 포함하는 100 개의 헤더 파일을 작성했습니다. 이 파일들 각각은 다음과 같이 보입니다 :

    <?php
    // /home/fbarnes/phpperf/hdr0.php
    require_once "../phpperf/common_hdr.php";
    
    ?>
    

    빠른 bash 해킹을 사용하여 다음과 같이 만들었습니다.

    for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
      echo "<?php
    // $i" > $i
      cat helper.php >> $i;
    done
    

    이 방법을 사용하면 require_once를 사용하여 쉽게 교체 할 수 있으며 헤더 파일을 포함 할 때 필요합니다. 그런 다음 app.php를 만들어 백개의 파일을로드했습니다. 이 모양은 다음과 같습니다.

    <?php
    
    // Load all of the php hdrs that were created previously
    for($i=0; $i < 100; $i++)
    {
      require_once "/home/fbarnes/phpperf/hdr$i.php";
    }
    
    // Read the /proc file system to get some simple stats
    $pid = getmypid();
    $fp = fopen("/proc/$pid/stat", "r");
    $line = fread($fp, 2048);
    $array = split(" ", $line);
    
    // write out the statistics; on RedHat 4.5 w/ kernel 2.6.9
    // 14 is user jiffies; 15 is system jiffies
    $cntr = 0;
    foreach($array as $elem)
    {
      $cntr++;
      echo "stat[$cntr]: $elem\n";
    }
    fclose($fp);
    
    ?>
    

    require_once 헤더와 헤더 파일을 사용하는 require 헤더를 대조했습니다 :

    <?php
    // /home/fbarnes/phpperf/h/hdr0.php
    if(!defined('CommonHdr'))
    {
      require "../phpperf/common_hdr.php";
      define('CommonHdr', 1);
    }
    
    ?>
    

    require vs. require_once를 사용하여 이것을 실행할 때 많은 차이점을 발견하지 못했습니다. 사실 나의 초기 테스트는 require_once가 약간 더 빠르다는 것을 암시하는 것처럼 보였지만, 필자는 그것을 반드시 믿지는 않는다. 나는 10000 개의 입력 파일로 실험을 반복했다. 여기서 나는 일관된 차이를 보았다. 테스트를 여러 번 실행했지만 결과는 비슷하지만 require_once를 사용하면 평균 30.8 개의 사용자 jiffies와 72.6 개의 시스템 jiffies를 사용합니다. 평균 39.4 사용자 jiffies 및 72.0 시스템 jiffies에서 요구 사용을 사용합니다. 따라서 require_once를 사용하여로드가 약간 더 낮게 나타납니다. 그러나 벽시계는 약간 증가합니다. 10,000 건의 require_once 호출은 평균 10.15 초를 사용하고 10,000 건의 호출은 평균 9.84 초를 사용합니다.

    다음 단계는 이러한 차이점을 조사하는 것입니다. strace를 사용하여 시스템 호출을 분석했습니다.

    require_once에서 파일을 열기 전에 다음 시스템 호출이 작성됩니다.

    time(NULL)                              = 1223772434
    lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
    lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=88, ...}) = 0
    time(NULL)                              = 1223772434
    open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3
    

    이는 요구 사항과 대조됩니다.

    time(NULL)                              = 1223772905
    lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
    lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=146, ...}) = 0
    time(NULL)                              = 1223772905
    open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3
    

    Tech Your Universe는 require_once가 더 많은 lstat64 호출을해야 함을 의미합니다. 그러나 둘 다 동일한 수의 lstat64 호출을합니다. 아마, 위의 코드를 최적화하기 위해 APC를 실행하지 않는다는 차이점이있을 수 있습니다. 그러나, 내가 한 다음은 전체 실행에 대한 strace의 출력을 비교했습니다.

    [fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out 
      190709 strace_1000r.out
      210707 strace_1000ro.out
      401416 total
    

    require_once를 사용할 때 헤더 파일 당 약 2 개의 시스템 호출이 효과적으로 발생합니다. 한가지 차이점은 require_once가 time () 함수에 대한 추가 호출을한다는 것입니다.

    [fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out 
    strace_1000r.out:20009
    strace_1000ro.out:30008
    

    다른 시스템 호출은 getcwd ()입니다.

    [fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out 
    strace_1000r.out:5
    strace_1000ro.out:10004
    

    이것은 hdrXXX 파일에서 참조 된 상대 경로로 결정 되었기 때문에 호출됩니다. 이 값을 절대적인 참조로 만들면 유일한 차이점은 코드에서 작성한 추가 시간 (NULL) 호출입니다.

    [fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out 
      190705 strace_1000r.out
      200705 strace_1000ro.out
      391410 total
    [fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
    strace_1000r.out:20008
    strace_1000ro.out:30008
    

    이것은 상대 경로가 아닌 절대 경로를 사용하여 시스템 호출 수를 줄일 수 있음을 의미합니다. 그 밖의 유일한 차이점은 무엇이 더 빠른지 비교하기 위해 코드를 계측하는 데 사용되는 것으로 보이는 시간 (NULL) 호출입니다.

    한 가지 다른 점은 APC 최적화 패키지에는 require_once 및 include_once 호출 (PHP 문서 참조)에 의한 시스템 호출 수가 감소한다고 주장하는 "apc.include_once_override"옵션이 있습니다.

    긴 게시물을 유감으로 생각합니다. 나는 호기심이 많다.

  4. ==============================

    4.코딩을 피하기 위해 이러한 코딩 관행에 대한 링크를 제공 할 수 있습니까? 내가 아는 한, 그것은 완전한 논쟁 거리가 아닙니다. 필자는 소스 코드를 직접 보지 않았지만 include와 include_once의 유일한 차이점은 include_once가 파일 이름을 배열에 추가하고 매번 배열을 검사한다는 것입니다. 그 배열을 정렬 된 상태로 유지하는 것은 쉬울 것입니다. 따라서 검색은 O (log n)이어야하며 중형 어플리케이션은 두 개 정도만 포함됩니다.

    코딩을 피하기 위해 이러한 코딩 관행에 대한 링크를 제공 할 수 있습니까? 내가 아는 한, 그것은 완전한 논쟁 거리가 아닙니다. 필자는 소스 코드를 직접 보지 않았지만 include와 include_once의 유일한 차이점은 include_once가 파일 이름을 배열에 추가하고 매번 배열을 검사한다는 것입니다. 그 배열을 정렬 된 상태로 유지하는 것은 쉬울 것입니다. 따라서 검색은 O (log n)이어야하며 중형 어플리케이션은 두 개 정도만 포함됩니다.

  5. ==============================

    5.더 좋은 방법은 객체 지향 접근법을 사용하고 __autoload ()를 사용하는 것입니다.

    더 좋은 방법은 객체 지향 접근법을 사용하고 __autoload ()를 사용하는 것입니다.

  6. ==============================

    6.* _once () 함수는 모든 상위 디렉토리를 포함하여 포함하고있는 파일이 이미 포함되어 있는지 확인합니다. 그것은 경기 침체의 원인의 일부입니다.

    * _once () 함수는 모든 상위 디렉토리를 포함하여 포함하고있는 파일이 이미 포함되어 있는지 확인합니다. 그것은 경기 침체의 원인의 일부입니다.

    Siege와 같은 도구를 벤치마킹에 사용하는 것이 좋습니다. 제안 된 모든 방법을 시도하고 응답 시간을 비교할 수 있습니다.

    Tech Your Universe의 require_once ()에 대한 추가 정보.

  7. ==============================

    7.PEAR2 위키 (존재했을 때)는 최소한 라이브러리 코드에 대해 자동 로딩을 위해 모든 require / include 지시어를 버리는 좋은 이유를 나열하는 데 사용되었습니다. phar와 같은 대체 패키징 모델이 수평선에있을 때, 이들은 엄격한 디렉토리 구조에 묶여 있습니다.

    PEAR2 위키 (존재했을 때)는 최소한 라이브러리 코드에 대해 자동 로딩을 위해 모든 require / include 지시어를 버리는 좋은 이유를 나열하는 데 사용되었습니다. phar와 같은 대체 패키징 모델이 수평선에있을 때, 이들은 엄격한 디렉토리 구조에 묶여 있습니다.

    업데이트 : wiki의 웹 아카이브 버전이 눈에 거슬 린 추악한 것처럼, 나는 아래의 가장 강력한 이유를 복사했습니다 :

  8. ==============================

    8.그것은 나쁜 기능을 사용하지 않습니다. 전반적인 코드 기반에서 언제 어떻게 사용하는지에 대한 잘못된 이해입니다. 아마도 오해의 개념에 조금 더 많은 내용을 추가 할 것입니다.

    그것은 나쁜 기능을 사용하지 않습니다. 전반적인 코드 기반에서 언제 어떻게 사용하는지에 대한 잘못된 이해입니다. 아마도 오해의 개념에 조금 더 많은 내용을 추가 할 것입니다.

    사람들은 require_once가 느린 함수라고 생각해서는 안됩니다. 코드를 편입해야합니다. require_once ()와 require ()의 속도는 문제가 아닙니다. 그것은 맹목적으로 그것을 사용하여 발생할 수있는 경고를 방해 성능에 대한이야. 컨텍스트에 대한 고려없이 광범위하게 사용되는 경우 엄청난 메모리 낭비 또는 낭비되는 코드가 발생할 수 있습니다.

    내가보기에는 정말 거대한 모 놀리 식 프레임 워크가 모든 잘못된 방법으로 require_once ()를 사용할 때, 특히 복잡한 객체 지향 환경에서 특히 그렇습니다.

    많은 라이브러리에서 볼 수있는 모든 클래스의 맨 위에서 require_once ()를 사용하는 예제를 살펴 보겠습니다.

    require_once("includes/usergroups.php");
    require_once("includes/permissions.php");
    require_once("includes/revisions.php");
    class User{
      //user functions
    }
    

    따라서 User 클래스는 다른 세 클래스를 모두 사용하도록 설계되었습니다. 공정! 하지만 방문자가 사이트를 탐색하고 로그인하지 않고 프레임 워크가로드되는 경우에는 어떻게됩니까? require_once ( "includes / user.php"); 매 요청마다.

    그것은 특별한 요청 중에 사용하지 않을 1 + 3 개의 불필요한 클래스를 포함합니다. 이것은 비 대한 프레임 워크가 5MB 이하가 아닌 요청 당 40MB를 사용하는 방식입니다.

    오용 될 수있는 다른 방법은 클래스가 다른 많은 사람들에 의해 재사용 될 때입니다! 헬퍼 함수를 ​​사용하는 약 50 개의 클래스가 있다고 가정 해보십시오. 헬퍼가로드 될 때 해당 클래스에서 헬퍼를 사용할 수 있도록하려면 다음을 수행하십시오.

    require_once("includes/helpers.php");
    class MyClass{
      //Helper::functions();//etc..
    }
    

    여기에 그 자체로 잘못된 것은 없습니다. 그러나 한 페이지 요청이 비슷한 종류의 15 개 클래스를 포함하면 발생합니다. require_once를 15 번 실행하거나 멋진 시각적 효과를 얻으려면 다음을 수행하십시오.

    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    require_once("includes/helpers.php");
    

    require_once ()를 사용하면 이러한 불필요한 행을 구문 분석하는 것 외에 기술적으로 14 번 해당 함수를 실행하는 성능에 영향을 미칩니다. 그 비슷한 문제를 가진 10 개의 다른 클래스들과 함께, 그것보다는 무의미한 반복적 인 코드의 100+ 라인을 설명 할 수 있습니다.

    그것으로 아마도 require ( "includes / helpers.php")를 사용할 가치가 있습니다. 대신 앱이나 프레임 워크의 부트 스트랩에서. 그러나 모든 것이 상대적이므로, 헬퍼 클래스의 가중치 대 사용 빈도가 require_once ()의 15-100 줄을 저장하는 데 가치가 있는지 여부는 모두 다릅니다. 그러나 주어진 요청에서 헬퍼 파일을 사용하지 않을 확률이 아무 것도 없다면 require는 반드시 메인 클래스에 있어야합니다. 각 클래스에 require_once를 별도로두면 자원 낭비가됩니다.

    require_once 함수는 필요할 때 유용하지만 모든 클래스를로드하기 위해 어디에서나 사용할 수있는 모 놀리 식 솔루션으로 간주해서는 안됩니다.

  9. ==============================

    9.require_once 및 include_once가 require 및 include (또는 다른 대안이있을 수도 있음)보다 느릴지라도 여기서 가장 미세한 수준의 미세 최적화에 대해 설명합니다. require_once와 같은 것을 걱정하는 것보다 저조한 루프 또는 데이터베이스 쿼리를 최적화하는 것이 훨씬 더 낫습니다.

    require_once 및 include_once가 require 및 include (또는 다른 대안이있을 수도 있음)보다 느릴지라도 여기서 가장 미세한 수준의 미세 최적화에 대해 설명합니다. require_once와 같은 것을 걱정하는 것보다 저조한 루프 또는 데이터베이스 쿼리를 최적화하는 것이 훨씬 더 낫습니다.

    이제는 require_once가 포함 된 코딩 방법을 허용한다는 주장을 제기 할 수 있습니다. 왜냐하면 포함 된 항목을 깨끗하게 정리하는 데주의 할 필요가 없기 때문에 기능 자체 및 특히 속도와 관련이 없습니다.

    분명히, 자동 로딩은 코드의 청결 함과 유지 보수의 편의를 위해 더 낫지 만, 이것이 속도와는 아무런 관련이 없음을 분명히하고 싶습니다.

  10. ==============================

    10.네, 보통 ol 'require ()보다 약간 비쌉니다. 요점은 당신이 중복을 포함하지 않을 정도로 코드를 체계적으로 관리 할 수 ​​있다면 * _once () 함수를 사용하지 말라는 것입니다.

    네, 보통 ol 'require ()보다 약간 비쌉니다. 요점은 당신이 중복을 포함하지 않을 정도로 코드를 체계적으로 관리 할 수 ​​있다면 * _once () 함수를 사용하지 말라는 것입니다.

    하지만 _once () 함수를 사용하면 앱을 종료하지 않습니다. 기본적으로, 귀하의 포함을 구성 할 필요가 없다는 핑계로 사용하지 마십시오. 어떤 경우에는 그것을 사용하는 것을 여전히 피할 수 없으며 큰 문제가 아닙니다.

  11. ==============================

    11.include, oli의 대안 및 __autoload ()를 사용하여 테스트합니다. 설치된 APC와 같은 것으로 테스트하십시오.

    include, oli의 대안 및 __autoload ()를 사용하여 테스트합니다. 설치된 APC와 같은 것으로 테스트하십시오.

    나는 상수를 사용하는 것이 일을 더 빠르게 할 수 있을지 의심 스럽다.

  12. ==============================

    12.PEAR 문서에는 require, require_once, include 및 include_once에 대한 권장 사항이 있다고 생각합니다. 나는 그 지침을 따른다. 귀하의 신청서가 더 분명합니다.

    PEAR 문서에는 require, require_once, include 및 include_once에 대한 권장 사항이 있다고 생각합니다. 나는 그 지침을 따른다. 귀하의 신청서가 더 분명합니다.

  13. ==============================

    13.속도와는 아무런 관련이 없습니다. 그것은 정상적으로 실패하는 것입니다.

    속도와는 아무런 관련이 없습니다. 그것은 정상적으로 실패하는 것입니다.

    require_once ()가 실패하면 스크립트가 완료됩니다. 그 밖의 것은 처리되지 않습니다. include_once ()를 사용하면 스크립트의 나머지 부분이 렌더링을 계속 시도하므로 사용자가 스크립트에서 실패한 부분에 잠재적으로 아무런 지혜가 없을 수 있습니다.

  14. ==============================

    14.필자가 개인적으로 require_once (또는 include_once)를 사용하는 것은 require_once가 이미 해당 파일을 포함하고 있으며 포함 된 두 파일의 오류를 억제하면 (예 : 함수 / 클래스 / 등의 중복 선언과 같은) 치명적인 오류를 발생시키지 않으므로 사용자를 확인하기 때문에 바람직하지 않습니다. .

    필자가 개인적으로 require_once (또는 include_once)를 사용하는 것은 require_once가 이미 해당 파일을 포함하고 있으며 포함 된 두 파일의 오류를 억제하면 (예 : 함수 / 클래스 / 등의 중복 선언과 같은) 치명적인 오류를 발생시키지 않으므로 사용자를 확인하기 때문에 바람직하지 않습니다. .

    파일을 포함해야하는지 알고 있어야합니다.

  15. from https://stackoverflow.com/questions/186338/why-is-require-once-so-bad-to-use by cc-by-sa and MIT license