Heim  >  Artikel  >  Backend-Entwicklung  >  PHP-Funktionsprinzip

PHP-Funktionsprinzip

angryTom
angryTomOriginal
2019-08-23 09:44:133004Durchsuche

PHP-Funktionsprinzip

Vorwort

In jeder Sprache sind Funktionen die grundlegendsten Bausteine. Was sind die Merkmale von PHP-Funktionen? Wie wird der Funktionsaufruf implementiert? Wie ist die Leistung von PHP-Funktionen? Irgendwelche Vorschläge zur Verwendung? In diesem Artikel wird versucht, diese Fragen zu beantworten, indem die Prinzipien analysiert und mit tatsächlichen Leistungstests kombiniert werden, um PHP-Programme besser zu schreiben und gleichzeitig die Implementierung zu verstehen. Gleichzeitig werden einige gängige PHP-Funktionen vorgestellt.

Klassifizierung von PHP-Funktionen

In PHP werden Funktionen bei horizontaler Unterteilung in zwei Kategorien unterteilt: Benutzerfunktion (integrierte Funktion) und interne Funktion. Ersteres sind einige Funktionen und Methoden, die vom Benutzer im Programm angepasst wurden, und letzteres sind verschiedene Bibliotheksfunktionen, die von PHP selbst bereitgestellt werden (z. B. Sprintf, Array_Push usw.). Benutzer können Bibliotheksfunktionen auch über Erweiterungsmethoden schreiben, die später vorgestellt werden. Die Benutzerfunktion kann in Funktion (Funktion) und Methode (Klassenmethode) unterteilt werden. In diesem Artikel werden diese drei Funktionen analysiert und getestet.

Empfohlene Tutorials: PHP-Video-Tutorial

Implementierung von PHP-Funktionen

Wie wird eine PHP-Funktion letztendlich ausgeführt?

Um diese Frage zu beantworten, werfen wir zunächst einen Blick auf den Prozess der Ausführung des PHP-Codes.

PHP-Funktionsprinzip

Wie Sie auf dem Bild sehen können, implementiert PHP einen typischen dynamischen Sprachausführungsprozess: Nachdem ein Code erhalten wurde, wurden Phasen wie die lexikalische Analyse und die Syntaxanalyse durchlaufen , der Quellcode Das Programm wird in Anweisungen (Opcodes) übersetzt, und dann führt die virtuelle ZEND-Maschine diese Anweisungen nacheinander aus, um den Vorgang abzuschließen. PHP selbst ist in C implementiert, daher sind die letztendlich aufgerufenen Funktionen alle C-Funktionen. Tatsächlich können wir PHP als eine in C entwickelte Software betrachten.

Aus der obigen Beschreibung ist nicht schwer zu erkennen, dass die Ausführung von Funktionen in PHP auch in Opcodes zum Aufruf übersetzt wird. Jeder Funktionsaufruf führt tatsächlich eine oder mehrere Anweisungen aus.

Zend beschreibt jede Funktion durch die folgende Datenstruktur

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;

Wobei Typ den Typ der Funktion angibt: Benutzerfunktion, integrierte Funktion, überladene Funktion. Common enthält die grundlegenden Informationen der Funktion, einschließlich Funktionsname, Parameterinformationen, Funktionsflags (normale Funktionen, statische Methoden, abstrakte Methoden) usw. Darüber hinaus gibt es für Benutzerfunktionen auch eine Funktionssymboltabelle, in der interne Variablen usw. aufgezeichnet werden, auf die später noch näher eingegangen wird. Zend verwaltet eine globale function_table, eine große Hash-Tabelle. Wenn eine Funktion aufgerufen wird, wird die entsprechende zend_function zunächst anhand des Funktionsnamens aus der Tabelle gefunden. Bei einem Funktionsaufruf bestimmt die virtuelle Maschine die aufrufende Methode anhand des Typs. Verschiedene Funktionstypen haben unterschiedliche Ausführungsprinzipien.

Eingebaute Funktionen

Integrierte Funktionen sind im Wesentlichen echte C-Funktionen. PHP erweitert sie anschließend Die endgültige Kompilierung wird zu einer Funktion mit dem Namen zif_xxxx, z. B. unserem gemeinsamen Sprintf, der der untersten Ebene entspricht, nämlich zif_sprintf. Wenn Zend bei der Ausführung eine integrierte Funktion findet, führt es einfach eine Weiterleitungsoperation durch.

Zend bietet eine Reihe von APIs zum Aufrufen, einschließlich Parametererfassung, Array-Operationen, Speicherzuweisung usw. Die Parameter der integrierten Funktion werden über die Methode zend_parse_parameters abgerufen. Für Parameter wie Arrays und Zeichenfolgen implementiert Zend flaches Kopieren, sodass diese Effizienz sehr hoch ist. Man kann sagen, dass die Effizienz der in PHP integrierten Funktionen nahezu mit der der entsprechenden C-Funktionen übereinstimmt, mit dem einzigen zusätzlichen Weiterleitungsaufruf.

Eingebaute Funktionen werden in PHP dynamisch geladen, sodass Benutzer entsprechend ihren Anforderungen auch schreiben können, was wir oft als Erweiterungen bezeichnen. ZEND bietet eine Reihe von APIs für die Erweiterungsverwendung

Benutzerfunktionen

Im Vergleich zu integrierten Funktionen haben benutzerdefinierte Funktionen, die über PHP implementiert werden völlig unterschiedliche Ausführungsprozesse und Umsetzungsprinzipien. Wie oben erwähnt, wissen wir, dass PHP-Code zur Ausführung in Opcodes übersetzt wird und Benutzerfunktionen keine Ausnahme bilden. Tatsächlich entspricht jede Funktion einem Satz von Opcodes, und dieser Satz von Anweisungen wird in zend_function gespeichert. Daher entspricht der Aufruf der Benutzerfunktion letztendlich der Ausführung einer Reihe von Opcodes.

Lokale Variablen speichern und Rekursion implementieren

Wir wissen, dass die Funktionsrekursion über den Stapel abgeschlossen wird. In PHP wird dazu eine ähnliche Methode verwendet. Zend weist jeder PHP-Funktion eine aktive Symboltabelle (active_sym_table) zu, um den Status aller lokalen Variablen in der aktuellen Funktion aufzuzeichnen. Alle Symboltabellen werden in Form eines Stapels verwaltet. Bei jedem Aufruf einer Funktion wird eine neue Symboltabelle zugewiesen und auf den Stapel verschoben. Wenn der Aufruf endet, wird die aktuelle Symboltabelle vom Stapel entfernt. Dadurch werden Zustandserhaltung und Rekursion erreicht.

Für die Stack-Wartung hat Zend es hier optimiert. Weisen Sie vorab ein statisches Array der Länge N zu, um den Stapel zu simulieren. Diese Methode wird auch häufig in unseren eigenen Programmen verwendet. Diese Methode vermeidet die durch jeden Aufruf verursachte Speicherzuweisung. ZEND bereinigt am Ende des Funktionsaufrufs lediglich die Symboltabellendaten oben im aktuellen Stapel.

Da die Länge des statischen Arrays N beträgt, verursacht das Programm keinen Stapelüberlauf, sobald die Funktionsaufrufebene N überschreitet. In diesem Fall weist zend die Symboltabelle zu und zerstört sie, was zu einer erheblichen Leistungseinbuße führt Abbau. In Zend beträgt der aktuelle Wert von N 32. Wenn wir PHP-Programme schreiben, ist es daher am besten, 32 Funktionsaufrufebenen nicht zu überschreiten. Wenn es sich um eine Webanwendung handelt, kann die Funktionsaufrufebene selbst natürlich tiefgreifend sein.

Übertragung von Parametern

Im Gegensatz zur integrierten Funktion, die zend_parse_params aufruft, um Parameter abzurufen, wird die Erfassung von Parametern in Benutzerfunktionen durch Anweisungen abgeschlossen . Die Anzahl der Parameter einer Funktion entspricht der Anzahl der Anweisungen. Spezifisch für die Implementierung handelt es sich um eine gewöhnliche Variablenzuweisung.

Aus der obigen Analyse geht hervor, dass im Vergleich zu den integrierten Funktionen die Leistung der Benutzerfunktion höher ist, da die Stapeltabelle selbst verwaltet wird und die Ausführung jeder Anweisung auch eine C-Funktion ist wird relativ viel schlimmer sein, wie später besprochen wird. Spezifische vergleichende Analyse. Wenn eine Funktion über eine entsprechende integrierte PHP-Funktion verfügt, versuchen Sie daher, die Funktion nicht selbst neu zu schreiben, um sie zu implementieren.

Klassenmethode

Das Ausführungsprinzip der Klassenmethode ist das gleiche wie das der Benutzerfunktion und wird auch in Opcodes übersetzt und aufgerufen Der Reihe nach. Die Klassenimplementierung wird von Zend mithilfe einer Datenstruktur zend_class_entry implementiert, die einige grundlegende Informationen zur Klasse speichert. Dieser Eintrag wird beim Kompilieren von PHP verarbeitet.

In der gemeinsamen Funktion von zend_function gibt es ein Mitglied namens „scope“, das auf den zend_class_entry der Klasse verweist, die der aktuellen Methode entspricht. Bezüglich der objektorientierten Implementierung in PHP werde ich hier keine detailliertere Einführung geben. In Zukunft werde ich einen speziellen Artikel schreiben, der das Prinzip der objektorientierten Implementierung in PHP detailliert beschreibt. Was die Funktion betrifft, ist das Implementierungsprinzip der Methode genau das gleiche wie das der Funktion, und ihre Leistung ist theoretisch ähnlich. Wir werden später einen detaillierten Leistungsvergleich durchführen.

Der Einfluss der Funktionsnamenlänge auf die Leistung

Testmethode

Für Namenslänge 1 , 2, 4, 8 und 16 Funktionen, testen und vergleichen Sie, wie oft sie pro Sekunde ausgeführt werden können, und bestimmen Sie den Einfluss der Länge des Funktionsnamens auf die Leistung

Die Testergebnisse sind wie gezeigt unten

PHP-Funktionsprinzip

Ergebnisanalyse

Aus der Abbildung ist ersichtlich, dass die Länge des Funktionsnamens immer noch a beträgt gewisse Auswirkungen auf die Leistung. Eine Funktion der Länge 1 und ein leerer Funktionsaufruf der Länge 16 haben einen Leistungsunterschied von 1x. Es ist nicht schwierig, den Grund durch die Analyse des Quellcodes zu finden. Wie oben erwähnt, fragt Zend beim Aufruf einer Funktion zunächst relevante Informationen über den Funktionsnamen in einer globalen Funktionstabelle ab, bei der es sich um eine Hash-Tabelle handelt. Je länger der Name ist, desto länger dauert die Abfrage zwangsläufig. Daher wird beim eigentlichen Schreiben eines Programms empfohlen, dass der Name einer Funktion, die mehrmals aufgerufen wird, nicht zu lang sein sollte

Obwohl die Länge des Funktionsnamens einen gewissen Einfluss auf die Leistung hat, ist es wichtig, wie groß sie ist es speziell? Dieses Problem sollte dennoch auf der Grundlage der tatsächlichen Situation berücksichtigt werden. Wenn eine Funktion selbst relativ komplex ist, hat sie keinen großen Einfluss auf die Gesamtleistung.

Ein Vorschlag besteht darin, Funktionen, die häufig aufgerufen werden und relativ einfache Funktionen haben, prägnante und prägnante Namen zu geben.

Der Einfluss der Anzahl der Funktionen auf die Leistung

Testmethode

Im Folgenden Drei Umgebungen Führen Sie den folgenden Funktionsaufruftest durch und analysieren Sie die Ergebnisse: 1. Das Programm enthält nur 1 Funktion. 2. Das Programm enthält 100 Funktionen. 3. Das Programm enthält 1000 Funktionen.

Testen Sie die Anzahl der Funktionen, die in diesen drei Situationen pro Sekunde aufgerufen werden können

Die Testergebnisse sind wie unten dargestellt

PHP-Funktionsprinzip

Ergebnisanalyse

Aus den Testergebnissen geht hervor, dass die Leistung in diesen drei Fällen nahezu gleich ist, wenn die Anzahl der Funktionen zunimmt minimal und kann ignoriert werden.

Aus der Analyse der Implementierungsprinzipien geht hervor, dass der einzige Unterschied zwischen mehreren Implementierungen der Teil der Funktionserfassung ist. Wie bereits erwähnt, werden alle Funktionen in einer Hash-Tabelle abgelegt, und die Sucheffizienz sollte unter verschiedenen Zahlen immer noch nahe bei O (1) liegen, sodass der Leistungsunterschied nicht groß ist.

Verbrauch verschiedener Arten von Funktionsaufrufen

Testmethode

Benutzerfunktion und Klasse auswählen Methode: Es gibt eine Art statische Methode und integrierte Funktion. Die Funktion selbst führt keine Aktionen aus und gibt hauptsächlich den Verbrauch leerer Funktionsaufrufe zurück. Die Testergebnisse sind die Anzahl der Ausführungen pro Sekunde

Um andere Effekte während des Tests zu entfernen, haben alle Funktionsnamen die gleiche Länge

Die Testergebnisse sind wie unten dargestellt

PHP-Funktionsprinzip

Ergebnisanalyse

Aus den Testergebnissen können wir ersehen, dass für die von Benutzern selbst geschriebenen PHP-Funktionen nein Egal um welchen Typ es sich handelt, ihr Wirkungsgrad ist nahezu gleich, etwa 280 W/s. Selbst bei Klimaanlagen ist die Effizienz der eingebauten Funktion erwartungsgemäß viel höher und erreicht 780 W/s, also dreimal so viel wie bei ersteren. Es ist ersichtlich, dass der Overhead von integrierten Funktionsaufrufen immer noch viel geringer ist als der von Benutzerfunktionen. Aus der vorherigen Prinzipanalyse ist ersichtlich, dass die Hauptlücke in Vorgängen wie der Initialisierung der Symboltabelle und dem Empfang von Parametern beim Aufruf der Benutzerfunktion liegt.

Leistungsvergleich zwischen integrierten Funktionen und Benutzerfunktionen

Testmethode

Built- in Funktionen und Benutzerfunktionen Für den Leistungsvergleich wählen wir hier mehrere häufig verwendete Funktionen aus und verwenden dann PHP-Funktionen, um dieselben Funktionen für den Leistungsvergleich zu implementieren.

Im Test haben wir ein typisches Beispiel aus Strings, Mathematik und Arrays zum Vergleich ausgewählt. Diese Funktionen sind String-Interception (substr), Dezimal-zu-Binär-Konvertierung (decbin) und Minimalwert (min) und Rückgaben alle Schlüssel im Array (array_keys).

Die Testergebnisse sind wie unten dargestellt

PHP-Funktionsprinzip

Ergebnisanalyse

Das kann Aus den Testergebnissen geht hervor, dass die Gesamtleistung integrierter Funktionen erwartungsgemäß viel höher ist als die normaler Benutzerfunktionen. Insbesondere bei Funktionen mit String-Operationen beträgt die Lücke eine Größenordnung. Ein Prinzip für die Verwendung von Funktionen besteht daher darin, dass Sie, wenn eine bestimmte Funktion über eine entsprechende integrierte Funktion verfügt, versuchen, diese zu verwenden, anstatt die PHP-Funktion selbst zu schreiben.

Bei einigen Funktionen mit einer großen Anzahl von Zeichenfolgenoperationen können Sie zur Verbesserung der Leistung die Verwendung von Erweiterungen zur Implementierung in Betracht ziehen. Zum Beispiel gängige Rich-Text-Filterung usw.

Leistungsvergleich mit C-Funktionen

Testmethode

Wir wählen jeweils 3 Funktionen für die Zeichenfolgenoperation und die arithmetische Operation zum Vergleich aus, und PHP verwendet Erweiterungen, um dies zu erreichen . Die drei Funktionen sind einfache einmalige arithmetische Operationen, Zeichenfolgenvergleiche und mehrere arithmetische Operationen.

Zusätzlich zu den beiden Funktionstypen wird auch die Leistung nach Entfernen des Funktionsklimatisierungsaufwands getestet. Einerseits werden die Leistungsunterschiede der beiden Funktionen (C und PHP) verglichen eingebaut) und andererseits wird der Funktionsverbrauch von der Seite überprüft

Der Testpunkt ist der Zeitverbrauch für die Ausführung von 100.000 Vorgängen

Die Testergebnisse sind wie unten dargestellt

PHP-Funktionsprinzip

Ergebnisanalyse

Der Unterschied zwischen den Kosten der integrierten Funktionen und C-Funktionen sind klein, nachdem die Auswirkungen der PHP-Funktionsklimatisierung entfernt wurden. Da die Funktionen immer komplexer werden, nähert sich die Leistung beider Parteien der gleichen an. Dies lässt sich anhand der vorherigen Funktionsimplementierungsanalyse leicht demonstrieren. Schließlich sind die integrierten Funktionen in C implementiert.

  Je komplexer die Funktion, desto kleiner ist der Leistungsunterschied zwischen C und PHP

Im Vergleich zu C ist der Overhead von PHP-Funktionsaufrufen viel höher und die Leistung einfacher Funktionen weist immer noch einen Rückgang auf bestimmte Auswirkungen. Daher sollten Funktionen in PHP nicht zu tief verschachtelt und gekapselt werden.

Pseudofunktionen und ihre Leistung

In PHP gibt es einige Funktionen, die zur Standardfunktionsverwendung gehören, aber die zugrunde liegende Implementierung ist vollständig Anders als echte Funktionsaufrufe gehören diese Funktionen zu keiner der drei oben genannten Funktionen. Sie sind im Wesentlichen ein separater Opcode, der hier als Pseudofunktion oder Befehlsfunktion bezeichnet wird.

Wie oben erwähnt, werden Pseudofunktionen genauso wie Standardfunktionen verwendet und scheinen die gleichen Eigenschaften zu haben. Aber wenn sie schließlich ausgeführt werden, werden sie von Zend in einer entsprechenden Anweisung (Opcode) zum Aufruf widergespiegelt, sodass ihre Implementierung näher an Operationen wie if, for und arithmetischen Operationen liegt.

Pseudofunktionen in PHP

1. isset

2. empty

3 unset

4 .evaluieren

Aus der obigen Einführung geht hervor, dass Pseudofunktionen im Vergleich zu gewöhnlichen Funktionen direkt in Anweisungen zur Ausführung übersetzt werden und dadurch ein geringerer Overhead durch einen Funktionsaufruf entsteht, sodass die Leistung besser ist. Einen Vergleich machen wir durch den folgenden Test. Sowohl Array_key_exists als auch isset können feststellen, ob ein Schlüssel im Array vorhanden ist.

PHP-Funktionsprinzip

Wie aus der Abbildung ersichtlich ist, ist die Leistung von isset im Vergleich zu array_key_exists ist viel höher, im Grunde etwa viermal so hoch wie beim ersteren, und selbst im Vergleich zu leeren Funktionsaufrufen ist die Leistung etwa 1-mal höher. Dies beweist auch, dass der Overhead von PHP-Funktionsaufrufen immer noch relativ groß ist.

Das obige ist der detaillierte Inhalt vonPHP-Funktionsprinzip. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn