Im folgenden Beispielmodul werden die Getter und Setter durch Hinzufügen anonymer Subroutinen zur Symboltabelle generiert. Wenn die Methoden auf diese Weise erstellt wurden, wird der resultierende Code funktional (in Bezug auf Verhalten, Geschwindigkeit usw.) mit einem Modul mit manuell geschriebenen Gettern und Settors gleichwertig sein, oder hat dieser Ansatz eine Art inhärente Haftung? (Ich habe ein grundlegendes Benchmarking durchgeführt und bisher keine Unterschiede festgestellt.)
%Vor% Es sollte keinen Unterschied in der Laufzeitleistung geben wenn der resultierende Code in beiden Fällen derselbe ist. Dies ist jedoch normalerweise nicht möglich, es sei denn, Sie verwenden den String eval
, um Ihre Unterprogramme zu erstellen. Zum Beispiel der von Ihnen bereitgestellte Code:
ist immer etwas langsamer als der Code, den Sie manuell geschrieben hätten:
%Vor% einfach, weil erstere den Wert der Variablen $ a erhalten muss, bevor sie ihn als Schlüssel für den Hash verwendet, während der letztere eine Konstante als Hash-Schlüssel verwendet. Außerdem ist shift
normalerweise tendenziell schneller als $_[0]
. Hier ist ein Benchmark-Code:
und die Ergebnisse auf meinem System:
%Vor%Sie sind so nah, dass Unterschiede im Lärm verloren gehen können, aber bei vielen Versuchen werden Sie sehen, dass die Variante "manuelle Verschiebung" die schnellste ist. Aber wie bei allen Microbenchmarks müssen Sie Ihr genaues Szenario auf Ihrer Hardware und Ihrer Perl-Version testen, um sicher zu gehen.
Und hier ist String Eval in den Mix geworfen.
%Vor%Es sollte genauso wie die "manuellen" Varianten sein, plus oder minus das statistische Rauschen. Meine Ergebnisse:
%Vor% Auch hier sind alle so nah, dass Sie sich große Mühe geben und viele Versuche durchführen müssen, um das Signal vom Lärm auszusortieren. Aber der Unterschied zwischen der Verwendung einer Konstante als Hash-Schlüssel und der Verwendung einer Variablen (deren Wert zuerst abgerufen werden muss) als Hash-Schlüssel sollte durchscheinen. (Die shift
-Optimierung ist ein separates Problem und wird in früheren oder zukünftigen Versionen von Perl wahrscheinlich in die eine oder andere Richtung geändert.)
Der Hauptnachteil von gut generierten Zugriffsmethoden ist, dass sie Tools besiegen, die auf statischer Analyse beruhen. Die automatische Vervollständigung Ihrer IDE-Methode zum Beispiel. Wenn dies Teil eines großen Projekts ist, empfehle ich Ihnen wärmstens, sich Elch anzusehen. Es ist die richtige Accessor-Generation (und vieles mehr). Es ist populär genug, dass Unterstützung IDEs hinzugefügt wird, so dass das oben erwähnte Problem zu gegebener Zeit verschwinden wird.
Es gibt viele Accessor-Generatoren auf CPAN, die einfach zu verwenden sind und mäßig effizienten Code generieren. Wenn die Leistung ein Problem darstellt, können Sie - sofern Sie die Zugriffsmethoden verwenden - nicht schneller als Klasse: : XSAccessor , da für die Accessoren hochoptimierter C / XS-Code verwendet wird.
Das Rollieren Ihres eigenen Accessor-generierenden Codes ist die schlechteste aller Optionen. Es vereitelt die statische Analyse für immer, ist wahrscheinlich ziemlich schwer zu lesen und führt möglicherweise neue Fehler ein.
Beide Ansätze führen dazu, dass zur Kompilierzeit eine Subroutinenreferenz in die Symboltabelle installiert wird. Das Verhalten und die Laufzeitleistung sind genau gleich. Es könnte einen sehr kleinen (d. H. Vernachlässigbaren) Unterschied in der Kompilierzeit geben.
Ein ähnlicher Ansatz besteht darin, Accessoren bei Bedarf über AUTOLOAD
zu generieren, was sich zur Laufzeit geringfügig auswirkt. Mit dem AUTOLOAD
-Ansatz kann auch das Verhalten von Dingen wie $object->can()
geändert werden.
Offensichtlich werden Methoden durch das Generieren von Methoden vor jeglicher Form statischer Analyse, einschließlich Tools wie Ctags, verborgen.
Das Laufzeitverhalten und die Leistung sollten ziemlich gleich sein (es sei denn, Sie tun etwas, das sich darum kümmert, ob die Methoden geschlossen sind oder nicht).
Bei einer großen Anzahl von Attributen wird es einen Unterschied in der Kompilierzeit und der Speicherbenutzung geben ... beides zugunsten der generierten Getter und Setter, nicht der manuell geschriebenen. Versuchen Sie zum Beispiel Folgendes:
%Vor%im Vergleich zu
%Vor%Der einzige Unterschied ist die Startzeit. Für einfache Codegenerierungsschemata ist der Unterschied schwer zu messen. Für komplexere Systeme kann es sich addieren.
Ein gutes Beispiel dafür ist Elch . Moose macht für Sie alle Arten von Code-Generierung, aber es hat einen erheblichen Einfluss auf die Startzeiten. Das ist ein Problem, dass die Moose-Entwickler an einem Schema zum Zwischenspeichern von generiertem Code
Betrachten Sie auch etwas wie Class :: Struct . Es erzeugt Code mit string eval (das letzte Mal habe ich überprüft). Trotzdem, weil es sehr einfach ist, verursacht es beim Start keine signifikante Verlangsamung.
Abgesehen von den ausgezeichneten Punkten, die die anderen erwähnt haben, möchte ich auch den Hauptnachteil hinzufügen, den ich gefunden habe: Sie alle erscheinen als anonyme Subroutinen im Profiler. Aus welchem Grund auch immer, Devel :: DProf weiß einfach nicht, wie er den Namen herausfinden soll.
>Nun würde ich hoffen , dass das neuere Devel :: NYTProf tun könnte eine bessere Arbeit - aber ich habe es nicht versucht.