source

require_once는 왜 이렇게 사용하기 나쁜가요?

factcode 2022. 12. 5. 21:14
반응형

require_once는 왜 이렇게 사용하기 나쁜가요?

나은 에 대해 모든 은 "PHP를 하지 마세요"라고 계속 있습니다.require_once속도 때문에.

왜 이러한가?

동일한 작업을 수행하는 적절한/더 나은 방법은 무엇입니까?require_once5를면면면면, 면면면 PHP 5 를를를를를를를 。

이미 "해결책 게시"가 있었고, 모든 면에서 잘못된 것이기 때문에 이 스레드는 나를 움츠리게 한다.다음을 열거합니다.

  1. PHP에서는 정의가 매우 비싸다.직접 찾거나 테스트할 수 있지만 PHP에서 글로벌 상수를 정의하는 유일한 효율적인 방법은 확장을 사용하는 것입니다.(클래스 상수는 실제로는 꽤 괜찮은 퍼포먼스입니다만, 이것은 무트 포인트입니다.2 때문에).

  2. 「 」를 사용하고 require_once(). 하지 않아도 됩니다.class_exists('Classname')첨부한 파일에 코드가 포함되어 있는 경우, 즉 절차적인 방법으로 사용하고 있는 경우, 전혀 그럴 이유가 없습니다.require_once()이 파일은 반드시 필요합니다.파일을 포함할 때마다 서브루틴 콜이라고 가정합니다.

은 많은 요.class_exists()포함 방법에 대해 설명합니다. 않지만 은 그럴 .require_once()일부 최신 버전의 PHP 이전에는 매우 비효율적이었습니다.하지만 이 문제는 해결되었습니다.또한 조건부 바이트 코드와 추가 메서드 호출을 위해 컴파일해야 하는 추가 바이트 코드가 내부 해시 테이블 체크를 훨씬 능가한다는 것이 제 주장입니다.

자, 인정하건대, 이 작업은 실행 시간이 너무 짧기 때문에 테스트하기가 어렵습니다.

여기서 고려해야 할 질문은 다음과 같습니다.일반적으로 PHP에서는 비용이 많이 듭니다.인터프리터는 인터프리터를 누를 때마다 해석 모드로 전환하고 opcode를 생성한 후 다시 점프해야 하기 때문입니다.100개 이상의 포함이 있는 경우 퍼포먼스에 영향을 미칩니다.require_once를 사용하는지 사용하지 않는지가 매우 중요한 질문인 이유는 opcode 캐시의 생활이 어려워지기 때문입니다.이에 대한 설명은 여기에서 찾을 수 있지만, 요약하면 다음과 같습니다.

  • 시간 중에 인지 알고 경우, 요청 수명 전체에 필요한 파일은 다음과 같습니다.require()opcode를 사용합니다.

  • opcode 캐시를 실행하고 있지 않으면 곤란한 상황에 처하게 됩니다.모든 포함을 1개의 파일로 인스톨 하는 것(개발중은 이것을 실시하지 말아 주세요)은, 확실히 시간의 해석에 도움이 됩니다만, 이것은 귀찮은 작업입니다.또, 요구시에 무엇을 포함할지를 정확하게 알 필요가 있습니다.

  • 자동 로드는 매우 편리하지만 속도가 느립니다. 따라서 포함을 수행할 때마다 자동 로직을 실행해야 합니다.실제로 한 가지 요청에 대해 여러 개의 특수 파일을 자동 로드하는 것은 큰 문제가 되지 않지만 필요한 모든 파일을 자동 로드하는 것은 아닙니다.

  • 10개의 포함(봉투 계산의 뒷면)이 있는 경우, 이 모든 것은 가치가 없습니다.데이터베이스 쿼리 등을 최적화해 주세요.

require_once and 그리고.include_once두 시스템은 이미 포함/필요한 것을 요구한다.둘 다 시스템이 이미 포함되거나 필요한 내용을 기록해야 합니다.모 모든 든*_oncecall은 로그를 확인하는 것을 의미합니다.거기서 분명히 가지 추가 작업이 이루어지고 있지만 전체 앱의 속도를 떨어뜨릴 만큼 충분한가?

정말 의심스럽네요오래된 하드웨어를 사용하거나 자주 사용하지 않는 한 그렇지 않습니다.

만약 당신이 수천개의 일을 하고 있다면*_once, 당신은 스스로 일을 더 가볍게 할 수 있습니다.간단한 앱의 경우 한 만 포함하면 충분하지만 여전히 재정의 오류가 발생할 경우 다음과 같은 오류가 발생할 수 있습니다.

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

I'll personally stick with the 저는 개인적으로 계속*_once하지만 바보 같은 밀리언 패스 벤치마크에서 두 가지 차이점을 알 수 있습니다.

                php                  hhvm
if defined      0.18587779998779     0.046600103378296
require_once    1.2219581604004      3.2908599376678

, 10~100배 느림require_oncerequire_once에는 hhvm 말씀드리지만, 이 실행 *_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.

궁금해서 Adam Backstrom과 Tech Your Universe의 링크를 확인했어요.이 문서에서는 require_once 대신 를 사용해야 하는 이유 중 하나를 설명합니다.하지만 그들의 주장은 내 분석에 부합하지 않았다.어디서 해결책을 잘못 분석했는지 알고 싶습니다.비교를 위해 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를 사용하는 것과 헤더 파일을 포함하는 것을 쉽게 바꿀 수 있습니다.그런 다음 100개의 파일을 로드하기 위한 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 with 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와 require_1회 실행 시 큰 차이는 없었습니다.사실 초기 테스트에서는 require_once가 약간 더 빠르다는 것을 암시하는 것 같았지만, 반드시 그렇다고는 생각하지 않습니다.저는 10,000개의 입력 파일로 실험을 반복했습니다.여기서 나는 일관된 차이를 보았다.테스트를 여러 번 실행했지만 결과는 비슷하지만 require_once use는 평균 30.8 사용자 jiffy와 72.6 시스템 jiffy를 사용합니다.require use는 평균 39.4 사용자 jiffy와 72.0 시스템 jiffy를 사용합니다.따라서 require_once를 사용하면 부하가 약간 낮아집니다.단, 벽시계 시간은 약간 증가합니다.10,000개의 require_once 콜은 평균 10.15초를 사용하여 완료되며 10,000개의 require 콜은 평균 9.84초를 사용합니다.

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

require_에서 파일을 열기 전에 다음 시스템 호출이 이루어집니다.

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

이것은 hdrXX 파일에서 참조되는 상대 경로를 결정했기 때문에 호출됩니다.이것을 절대 참조로 하면, 유일한 차이는, 코드로 발신된 추가 시간(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 호출의 수를 줄이는 "apc.include_once_override"라는 옵션이 있다는 것입니다(PHP 문서 참조).

피하라고 하는 코딩 관행에 대한 링크를 제공해 주실 수 있습니까?내가 아는 한, 그건 전혀 문제가 되지 않습니다.제가 직접 소스코드를 살펴본 적은 없지만, 제 생각엔 두 사람 사이의 유일한 차이점은include ★★★★★★★★★★★★★★★★★」include_once라는 것이다.include_once는, 그 파일명을 어레이에 추가해, 매번 어레이를 체크합니다.어레이를 정렬하는 것은 간단하기 때문에 O(log n)로 검색해야 합니다.또한 중간 크기의 어플리케이션에도 포함된 것은 몇 개뿐입니다.

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

PEAR2 Wiki(존재하는 경우)에서는 적어도 라이브러리 코드에 대해서는 자동 로딩에 찬성하기 위해 모든 필수/포함 디렉티브를 포기해야 하는 타당한 이유를 나열했습니다.par와 같은 대체 패키징 모델이 곧 등장할 예정이기 때문에, 이러한 구조는 경직된 디렉토리 구조로 이어집니다.

업데이트: Wiki의 웹 아카이브 버전이 눈에 띄게 보기 흉하기 때문에 아래에 가장 설득력 있는 이유를 복사했습니다.

  • (PEAR) 패키지를 사용하려면 include_path가 필요합니다.이로 인해 다른 응용 프로그램 내에 PEAR 패키지를 자체 include_path로 번들하여 필요한 클래스를 포함하는 단일 파일을 만들고 광범위한 소스 코드 변경 없이 PEAR 패키지를 par 아카이브로 이동하는 것이 어려워집니다.
  • top-level require_once가 조건부 require_once와 혼합되면 PHP 6에 번들되는 APC 등의 opcode 캐시에 의해 캐시되지 않는 코드가 발생할 수 있습니다.
  • relative require_once에서는 include_path가 올바른 값으로 이미 설정되어 있어야 하므로 적절한 include_path가 없으면 패키지를 사용할 수 없습니다.

그것은 나쁜 기능을 사용하는 것이 아니다.전체 코드 베이스에서 사용하는 방법과 시기에 대한 잘못된 이해입니다.오해할 수 있는 개념에 조금 더 컨텍스트를 추가하겠습니다.

함수라고 안 .require_once는 한 번이면 됩니다.어떤 식으로든 코드를 포함해야 합니다. require_once() ★★require()의 속도는 문제가 되지 않습니다.맹목적으로 사용할 경우 발생할 수 있는 경고를 방해하는 성능에 대한 것입니다.콘텍스트를 고려하지 않고 광범위하게 사용하면 메모리 낭비나 코드 낭비를 일으킬 수 있습니다.

가 사용되었을 때 입니다.require_once()특히 복잡한 객체 지향(OO) 환경에서 잘못된 방식으로 모든 작업을 수행할 수 있습니다.

사용 예를 들어 보겠습니다.require_once()많은 라이브러리에서 볼 수 있는 모든 클래스의 최상위에 있습니다.

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

그...User 세 클래스를 모두 .class 른 、 3 개 、 3 개 class 、 3 class 、 class class class class class 。좋!!

도 하지 않은 로딩되면 됩니까?require_once("includes/user.php");모든 요구에 대응합니다.

여기에는 1+3의 불필요한 클래스가 포함되어 있어 특정 요청 중에는 사용하지 않습니다.따라서 비대해진 프레임워크는 요구당 5MB 이하가 아닌 40MB를 사용하게 됩니다.


다른 방법으로 오용될 수 있는 것은 수업이 다른 많은 사람들에 의해 재사용되는 것이다!를 들어 의 클래스가 있고 가 있다고 가정해봅시다.helper ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.helpers이러한 클래스가 로딩되면, 다음의 정보를 얻을 수 있습니다.

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

여기 그 자체로는 아무 문제가 없다.단, 한 페이지 요청에 15개의 유사한 클래스가 포함되어 있는 경우..require_once의 경우: 15회, "15회", "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");대신 애플리케이션 또는 프레임워크의 부트스트랩에서 사용할 수 있습니다.그러나 모든 것은 상대적이기 때문에 무게와 사용 빈도에 따라 달라집니다.helpers는 15-100줄의 가 있습니다.require_once(), </FONT CHANGE:>를 하지 않을 이 있는 </FONT CHANGE:>를 사용해 주세요.helpersnone 이며, 으로 none 이 됩니다.require당연히 메인 클래스여야 합니다. 있다require_once각 클래스에서 개별적으로 자원 낭비가 됩니다.


require_once함수는 필요할 때 유용하지만 모든 클래스를 로드하기 위해 모든 곳에서 사용하는 단일 솔루션으로 간주해서는 안 됩니다.

*_once()functions는 모든 상위 디렉토리를 stat하여 포함하는 파일이 이미 포함된 파일과 동일하지 않은지 확인합니다.그것이 경기둔화의 이유 중 하나이다.

벤치마킹에는 Seague와 같은 도구를 사용할 것을 권장합니다.제안된 모든 방법론을 사용해 보고 응답 시간을 비교할 수 있습니다.

것은 이쪽require_once()Tech Your url Universe에 있습니다.

require_once ★★★★★★★★★★★★★★★★★」include_once 보다 느리다require ★★★★★★★★★★★★★★★★★」include(또는 어떤 대안이 존재하든) 여기서 말하는 것은 최소 수준의 미세 최적화에 관한 것입니다.다음과 같은 문제를 고민하는 것보다 제대로 작성되지 않은 루프 또는 데이터베이스 쿼리를 최적화하는 데 시간을 할애하는 것이 훨씬 좋습니다.require_once.

그럼 이제 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 이런 을 할 수 .require_once를 포함한 것을 깨끗하고 정리된 상태로 유지하는 것에 주의를 기울일 필요가 없기 때문에, 부실한 코딩 관행이 가능하게 됩니다.그러나 이는 함수 자체와 관련이 없으며, 특히 속도와는 관계가 없습니다.

코드 청결과 유지보수의 용이성을 위해 오토로딩이 좋은 것은 분명하지만, 속도와는 무관하다는 것을 분명히 하고 싶습니다.

include를 사용하여 oli의 대체 및 __autoload()를 테스트하고 APC와 같은 것을 설치한 상태에서 테스트합니다.

상수 사용으로 속도가 빨라질 것 같진 않네요.

네, 보통 ol' 요구보다 조금 더 비쌉니다.포인트가 되는 것은 중복되지 않도록 코드를 정리할 수 있다면 *_once() 함수를 사용하지 않는 것입니다.이것은 사이클을 절약할 수 있기 때문입니다.

그러나 _once() 함수를 사용해도 응용 프로그램은 종료되지 않습니다.기본적으로, 포함 항목을 정리할 필요가 없는 핑계로 사용하지 마십시오.그래도 어쩔 수 없는 경우도 있고, 큰 일도 아닙니다.

PEAR 문서에는 require_once, include 및 include_once에 대한 권장사항이 있다고 생각합니다.저는 그 가이드라인을 따르고 있습니다.당신의 지원서가 더 명확할 것입니다.

속도와는 상관이 없어요.우아하게 실패하는 거야

require_once()가 실패하면 스크립트는 완료됩니다.그 외에는 아무것도 처리되지 않습니다.include_once()를 사용하면 스크립트의 나머지 부분은 렌더링을 계속 시도하기 때문에 사용자는 스크립트에서 실패한 것에 대해 전혀 이해하지 못할 수 있습니다.

개인적으로는 require_once(또는 include_once)를 사용하는 것은 잘못된 관행이라고 생각합니다.이는 require_once가 해당 파일을 이미 포함했는지 확인하고 이중으로 포함된 파일의 오류를 억제하여 치명적인 오류(함수/클래스 중복 선언 등)를 발생시키기 때문입니다.

파일을 포함할 필요가 있는지 알아야 합니다.

언급URL : https://stackoverflow.com/questions/186338/why-is-require-once-so-bad-to-use

반응형