>  기사  >  백엔드 개발  >  PHP 함수 원리

PHP 함수 원리

angryTom
angryTom원래의
2019-08-23 09:44:133004검색

어떤 언어에서든 함수는 가장 중요한 기본 구성 요소입니다. PHP 함수의 특징은 무엇입니까? 함수 호출은 어떻게 구현되나요? PHP 기능의 성능은 어떻습니까? 사용 방법에 대한 제안이 있습니까? 이 기사에서는 구현을 이해하면서 PHP 프로그램을 더 잘 작성하기 위해 원리를 분석하고 실제 성능 테스트와 결합하여 이러한 질문에 답하려고 노력할 것입니다. 동시에 몇 가지 일반적인 PHP 기능이 소개됩니다.

PHP 함수 원리

PHP 함수 분류 PHP에서 함수를 가로로 나누면 두 가지 범주로 나뉩니다. 사용자 기능(내장 기능)과 내부 기능(내장 기능). 전자는 프로그램 내에서 사용자가 맞춤화한 일부 함수와 메소드이고, 후자는 PHP 자체에서 제공하는 다양한 라이브러리 함수(예: sprintf, array_push 등)입니다. 사용자는 나중에 소개될 확장 메서드를 통해 라이브러리 함수를 작성할 수도 있습니다. 사용자 기능은 함수(function)와 메소드(class method)로 나눌 수 있습니다. 본 글에서는 이 세 가지 기능을 각각 분석하고 테스트하겠습니다.

추천 튜토리얼:

PHP 비디오 튜토리얼

PHP 함수 구현 PHP 함수는 최종적으로 어떻게 실행되나요? 이 질문에 답하기 위해 먼저 PHP 코드가 실행되는 과정을 살펴보겠습니다.

그림에서 볼 수 있듯이 PHP는 일반적인 동적 언어 실행 프로세스를 구현합니다. 코드 조각을 얻은 후 어휘 분석을 거쳐 구문 분석 대기 단계 후에 소스 프로그램은 명령(opcode)으로 변환되고 ZEND 가상 머신은 이러한 명령을 순서대로 실행하여 작업을 완료합니다. PHP 자체가 C로 구현되어 있기 때문에 최종적으로 호출되는 함수는 모두 C 함수입니다. 사실 PHP는 C로 개발된 소프트웨어라고 볼 수 있습니다.

위의 설명에서 PHP의 함수 실행도 호출을 위한 opcode로 변환된다는 것을 쉽게 알 수 있습니다.

각 함수에 대해 zend는 다음 데이터 구조로 설명됩니다.

typedef union _zend_function {
    zend_uchar type;    /* MUST be the first element of this struct! */
    struct {
        zend_uchar type;  /* never used */
        char *function_name;
        zend_class_entry *scope;
        zend_uint fn_flags;
        union _zend_function *prototype;
        zend_uint num_args;
        zend_uint required_num_args;
        zend_arg_info *arg_info;
        zend_bool pass_rest_by_reference;
        unsigned char return_reference;
    } common;

    zend_op_array op_array;
    zend_internal_function internal_function;
} zend_function;

typedef struct _zend_function_state {
    HashTable *function_symbol_table;
    zend_function *function;
    void *reserved[ZEND_MAX_RESERVED_RESOURCES];
} zend_function_state;

여기서 유형은 함수 유형(사용자 함수, 내장 함수, 오버로드된 함수)을 나타냅니다. 공통에는 함수 이름, 매개변수 정보, 함수 플래그(일반 함수, 정적 메서드, 추상 메서드) 등 함수의 기본 정보가 포함됩니다. 또한, 사용자 함수에 대해서는 내부 변수 등을 기록하는 함수 기호 테이블도 있는데 이에 대해서는 나중에 자세히 설명하겠습니다. Zend는 대규모 해시 테이블인 전역 function_table을 유지 관리합니다. 함수가 호출되면 먼저 함수 이름을 기반으로 테이블에서 해당 zend_function을 찾습니다. 함수를 호출할 때 가상 머신은 유형에 따라 호출 방법을 결정합니다. 함수 유형에 따라 실행 원칙이 다릅니다.

PHP 함수 원리

내장 함수

내장 함수는 본질적으로 실제 C 함수입니다. 내장 함수, PHP가 최종적으로 컴파일된 후 zif_xxxx라는 함수로 확장됩니다. 예를 들어 공통 sprintf는 맨 아래 레이어인 zif_sprintf에 해당합니다. Zend가 실행 중일 때 내장 함수를 찾으면 단순히 전달 작업을 수행합니다.

Zend는 매개변수 획득, 배열 작업, 메모리 할당 등을 포함하여 호출을 위한 일련의 API를 제공합니다. 내장 함수의 매개변수는 zend_parse_parameters 메소드를 통해 얻습니다. 배열, 문자열 등의 매개변수에 대해 zend는 얕은 복사를 구현하므로 이 효율성이 매우 높습니다. PHP 내장 함수의 경우 추가 전달 호출만 제외하면 효율성은 해당 C 함수의 효율성과 거의 동일하다고 말할 수 있습니다.

내장 함수는 PHP에서 동적으로 로드되므로 사용자는 필요에 따라 해당 기능을 작성할 수도 있습니다. 이를 흔히 확장이라고 부릅니다. ZEND는 확장 기능을 위한 일련의 API를 제공합니다사용자 기능

내장 기능과 비교하면 사용자 PHP를 통해 구현된 정의 함수는 실행 프로세스와 구현 원칙이 완전히 다릅니다. 위에서 언급했듯이 우리는 PHP 코드가 실행을 위해 opcode로 변환된다는 것을 알고 있으며 사용자 함수도 예외는 아닙니다. 실제로 각 함수는 opcode 세트에 해당하며 이 명령 세트는 zend_function에 저장됩니다. 따라서 사용자 함수에 대한 호출은 궁극적으로 일련의 opcode 실행에 해당합니다.

지역 변수 저장 및 재귀 구현

우리는 스택을 통해 함수 재귀가 완료된다는 것을 알고 있습니다. PHP에서는 이를 달성하기 위해 유사한 방법이 사용됩니다. Zend는 활성 기호 테이블(active_sym_table)을 각 PHP 함수에 할당하여 현재 함수의 모든 지역 변수 상태를 기록합니다. 모든 기호 테이블은 함수가 호출될 때마다 새로운 기호 테이블이 할당되어 스택에 푸시되는 형태로 유지됩니다. 호출이 끝나면 현재 기호 테이블이 스택에서 팝됩니다. 이는 상태 보존 및 재귀를 달성합니다.

스택 유지 관리와 관련하여 zend는 여기에서 최적화했습니다. 스택을 시뮬레이션하기 위해 길이 N의 정적 배열을 사전 할당합니다. 정적 배열을 통해 동적 데이터 구조를 시뮬레이션하는 이 방법은 각 호출로 인한 메모리 할당을 방지하는 데도 사용됩니다. ZEND는 함수 호출이 끝날 때 현재 스택의 맨 위에 있는 기호 테이블 데이터를 정리합니다.

정적 배열 길이가 N이므로 함수 호출 수준이 N을 초과하면 프로그램에서 스택 오버플로가 발생하지 않습니다. 이 경우 zend는 기호 테이블을 할당하고 삭제하므로 오류가 발생합니다. 많은 성능 저하. zend에서 N의 현재 값은 32입니다. 따라서 PHP 프로그램을 작성할 때 함수 호출 수준이 32개를 초과하지 않는 것이 가장 좋습니다. 물론 웹 애플리케이션이라면 함수 호출 수준 자체가 깊을 수도 있다.

매개변수 전달

은 매개변수를 얻기 위해 zend_parse_params를 호출하는 내장 함수와 다릅니다. 사용자 기능의 매개변수는 검색이 지침을 통해 수행됩니다. 함수가 갖는 매개변수의 수는 명령어의 수에 해당합니다. 구현에 있어서는 일반적인 변수 할당입니다.

위의 분석을 보면 내장 함수와 비교해 보면 스택 테이블이 스스로 유지되고, 각 명령어의 실행도 C 함수이기 때문에, 나중에 구체적인 비교 분석이 있을 예정입니다. 따라서 함수에 해당 PHP 내장 함수가 있는 경우 이를 구현하기 위해 함수를 직접 다시 작성하지 마십시오.

클래스 메소드

클래스 메소드의 실행 원리는 사용자 함수의 실행 원리와 동일하며, 또한 순서대로 호출되는 opcode로 변환됩니다. 클래스 구현은 클래스와 관련된 일부 기본 정보를 저장하는 zend_class_entry 데이터 구조를 사용하여 zend에 의해 구현됩니다. 이 항목은 PHP가 컴파일될 때 처리됩니다.

zend_function의 공통점에는 현재 메서드에 해당하는 클래스의 zend_class_entry를 가리키는 범위라는 멤버가 있습니다. PHP의 객체지향 구현에 대해서는 여기서 더 자세히 소개하지 않겠습니다. 앞으로는 PHP의 객체지향 구현 원리를 자세히 설명하는 특별한 글을 쓸 예정입니다. 함수에 관한 한, 메소드의 구현 원리는 함수의 구현 원리와 완전히 동일하며, 이론적으로 성능도 유사합니다. 자세한 성능 비교는 나중에 하겠습니다.

함수 이름 길이가 성능에 미치는 영향

테스트 방법 # 🎜🎜#

이름 길이가 1, 2, 4, 8, 16인 함수를 비교하고, 초당 실행할 수 있는 횟수를 테스트 및 비교하고, 함수 이름 길이가 성능에 미치는 영향을 확인합니다.# 🎜🎜#

테스트 결과는 아래와 같습니다

PHP 함수 원리

결과 분석#🎜 🎜#

그림에서 볼 수 있듯이 함수 이름의 길이는 여전히 성능에 일정한 영향을 미칩니다. 길이가 1인 함수와 길이가 16인 빈 함수 호출의 성능 차이는 1배입니다. 위에서 언급한 것처럼 함수가 호출되면 zend는 먼저 해시 테이블인 전역 function_table에 있는 함수 이름을 통해 관련 정보를 쿼리합니다. 필연적으로 이름이 길수록 쿼리에 더 많은 시간이 소요됩니다. 따라서 실제로 프로그램을 작성할 때 여러 번 호출되는 함수의 이름은 너무 길지 않는 것이 좋습니다

함수 이름의 길이가 성능에 어느 정도 영향을 주긴 하지만, 어떻게 구체적으로 큰가요? 이 문제는 실제 상황에 따라 고려해야 할 부분입니다. 기능 자체가 비교적 복잡한 경우에는 전체 성능에 큰 영향을 미치지 않습니다.

한 가지 제안은 여러 번 호출되고 비교적 간단한 기능을 갖는 함수에 간결하고 간결한 이름을 지정하는 것입니다.

기능 개수가 성능에 미치는 영향

테스트 방법#🎜🎜 ## 🎜🎜#

다음 세 가지 환경에서 함수 호출 테스트를 수행하고 결과를 분석합니다. 1. 프로그램에는 1개의 함수만 포함되어 있습니다. 2. 프로그램에는 100개의 함수가 포함되어 있습니다. 3. 프로그램에는 1000개의 함수가 포함되어 있습니다.

이 세 가지 상황에서 초당 호출할 수 있는 함수 수를 테스트해 보세요

테스트 결과는 아래와 같습니다

Result Analysis

PHP 함수 원리

테스트 결과에서 알 수 있듯이 이 세 가지 경우의 성능은 다음과 같습니다. 거의 동일하며 기능의 수가 증가합니다. 성능 저하가 최소화되어 무시할 수 있습니다.

 구현 원리 분석에서 여러 구현 간의 유일한 차이점은 기능 획득 부분입니다. 앞에서 언급했듯이 모든 함수는 해시 테이블에 배치되어 있으며, 다른 숫자에서도 검색 효율성은 여전히 ​​O(1)에 가까워야 하므로 성능 차이는 크지 않습니다.

다양한 유형의 함수 호출 소비

테스트 방법

사용자 함수, 클래스 메서드, 정적 메서드, 내장 함수 중 하나를 선택합니다. 함수 자체는 아무것도 하지 않고 직접 반환합니다. 주로 빈 함수 호출을 테스트합니다. 테스트 결과는 초당 실행 횟수입니다

테스트 중 다른 효과를 제거하기 위해 모든 함수 이름의 길이는 동일합니다

테스트 결과는 아래와 같습니다

PHP 함수 원리

결과 분석

테스트 결과에서 볼 수 있듯이 사용자의 경우 어떤 유형의 PHP 함수를 작성하든 효율성은 거의 동일하며 약 280w/s입니다. 예상했던 대로 에어컨의 경우에도 내장 기능의 효율이 훨씬 높아져 기존보다 3배 높은 780w/s에 이른다. 내장 함수 호출의 오버헤드는 여전히 사용자 함수의 오버헤드보다 훨씬 낮다는 것을 알 수 있습니다. 앞선 원리 분석을 통해 사용자 함수 호출 시 심볼 테이블을 초기화하고 매개변수를 받는 등의 작업에 가장 큰 공백이 있음을 알 수 있다.

내장 함수와 사용자 함수의 성능 비교

테스트 방법

여기에서는 일반적으로 사용되는 여러 함수를 선택하고 PHP를 사용하여 구현합니다. 성능을 테스트하는 동일한 기능.

테스트에서는 비교를 위해 문자열, 수학, 배열 중에서 일반적인 것을 선택했습니다. 이러한 함수는 문자열 가로채기(substr), 10진수를 이진수로 변환(decbin), 최소값(min)이며 모든 키를 반환합니다. 배열(array_keys).

테스트 결과는 아래와 같습니다

PHP 함수 원리

결과 분석

테스트 결과에서 예상한 대로 내장 함수의 전반적인 성능이 기존 내장 함수보다 훨씬 높은 것을 알 수 있습니다. 일반 사용자 기능. 특히 문자열 연산과 관련된 함수의 경우 그 차이는 1차수에 이릅니다. 따라서 함수 사용의 한 가지 원칙은 특정 함수에 해당하는 내장 함수가 있으면 PHP 함수를 직접 작성하는 대신 이를 사용해 보는 것입니다.

 많은 수의 문자열 연산을 포함하는 일부 기능의 경우 성능 향상을 위해 확장 기능 사용을 고려할 수 있습니다. 예를 들어 일반적인 서식 있는 텍스트 필터링 등이 있습니다.

 C 함수와의 성능 비교

테스트 방법

 문자열 연산과 산술 연산에 대해 각각 3개의 함수를 선택하여 비교하였고, PHP는 확장 기능으로 구현했습니다. 세 가지 기능은 간단한 일회성 산술 연산, 문자열 비교 및 ​​다중 산술 연산입니다.

두 가지 유형의 함수 외에도 에어컨 오버헤드 함수를 제거한 후 성능을 테스트하는 한편, 두 함수(C 및 PHP 내장)의 성능 차이를 비교합니다. 한편, 공조 기능의 소모량을 검증합니다

 테스트 포인트는 100,000회 연산을 실행하는 데 소요되는 시간입니다

테스트 결과는 아래와 같습니다

PHP 함수 원리

결과 분석

내장 함수와 C 함수의 오버헤드 차이는 PHP 함수 에어컨의 영향을 제거한 후 작습니다. 함수가 점점 더 복잡해짐에 따라 두 당사자의 성능은 동일하게 접근합니다. 이는 앞선 함수 구현 분석을 통해 쉽게 확인할 수 있다. 결국 내장 함수는 C로 구현된다.

 함수가 복잡할수록 C와 PHP 사이의 성능 격차는 줄어듭니다

 C에 비해 PHP 함수 호출의 오버헤드는 훨씬 높으며, 간단한 함수의 성능은 여전히 ​​일정한 영향을 미칩니다. 따라서 PHP의 함수는 너무 깊게 중첩되거나 캡슐화되어서는 안 됩니다.

의사 함수 및 해당 성능

PHP에는 표준 함수 사용법인 일부 함수가 있지만 기본 구현은 실제 함수 호출과 완전히 다릅니다. 이러한 함수는 위에서 언급되지 않았습니다. 유형의 함수는 본질적으로 별도의 opcode이며 여기서는 의사 함수 또는 명령 함수라고 합니다.

 위에서 언급했듯이 의사 함수는 표준 함수와 동일하게 사용되며 동일한 특성을 갖는 것처럼 보입니다. 그러나 최종적으로 실행되면 zend에 의해 호출을 위한 해당 명령(opcode)에 반영되므로 구현은 if, for 및 산술 연산과 같은 연산에 더 가깝습니다.

php

 1.isset

  2.empty

  3、unset

 4.eval

의 의사 함수

 위의 소개를 통해 의사 함수는 실행 명령으로 직접 변환되므로 일반 함수에 비해 함수 호출로 인한 오버헤드가 하나 적으므로 성능이 더 좋아진다는 것을 알 수 있습니다. 다음 테스트를 통해 비교해보겠습니다. Array_key_exists와 isset 모두 배열에 키가 존재하는지 여부를 확인할 수 있습니다. 그림에서 볼 수 있듯이 array_key_exists와 비교하면 isset의 성능은 기본적으로 전자보다 4배 더 높습니다. 약 몇 배, 심지어 빈 함수 호출과 비교해도 성능이 약 1배 더 높습니다. 이는 또한 PHP 함수 호출의 오버헤드가 여전히 상대적으로 크다는 것을 증명합니다.

위 내용은 PHP 함수 원리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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