2015-05-15 19 views
8

Betrachten Sie den folgenden Besucher für einen einfachen Sprachinterpreter.Überspringt "akzeptieren", wo Typ bekannt ist, eine gültige Optimierung für das Besuchermuster?

Der Vollständigkeit halber füge ich etwas Code hinzu, der notwendige Implementierungsdetails gibt (Sie können die Frage überspringen und direkt lesen).

VarStat ::== var Ident = Exp; 
Exp ::== Exp + Exp | IntLiteral | Ident 
IntLiteral ::== [0-9]{0,8} 
Ident ::== [a-zA-Z]+ 

eine gültige Sprach Instanz

var x = x+y+4; 

abstrakte Weise die VarStat Knoten darzustellen, ist das folgende::

public interface Visitable{ 
    void accept(Visitor v); 
} 

public class VarStat implements Visitable{ 
    Ident i; 
    Exp e; 

    public VarStat(Ident id, Exp ex){ 
     i = id; 
     e = ex; 
    } 

    public Ident getIdent() { return i; } 
    public Exp getExp() { return e; } 

    @Override 
    public void accept(Visitor v){ 
     v.visit(this); 
    } 
} 

public interface Exp extends Visitable{ 

} 

public class Ident implements Exp{ 
    @Override 
    public void accept(Visitor v){ 
     v.visit(this); 
    } 
} 

a var Anweisung wird so definiert

.    _____VarStat _____ 
.   / /| \  \ 
.   / / | \  \ 
.   / / | \  \ 
.   "var" Ident "=" Exp ";" 

Die Frage

Die übliche VisitorPattern Anwendung

void visit(VarStat vs){ 
    vs.getIdent().accept(this); 
    vs.getExp().accept(this); 
    //... 
} 

aber wäre, da ich "Ident" weiß vom Typ Ident eine mögliche Optimierung ist

void visit(VarStat vs){ 

    visit(vs.getIdent()); 
    vs.getExp().accept(this); 
    //... 
} 

Das wäre Überspringe 2 Methodenaufrufe, um die Leistung zu verbessern (tatsächlich gibt es in meinem Szenario einen schönen Schub).

Wird dies als Konstruktionsfehler angesehen, der zu zukünftigen Problemen führen könnte?

+1

_actually gibt es einen schönen Schub mein Szenario_. Ja wirklich ? Wie? – Jayan

+0

Ja, ich möchte auch die gemessenen Zahlen sehen. –

+0

15% schneller. Ausgenutzt an 20 Orten über 25 Methoden (der echte Besucher hat momentan 25 Methoden). Auch einfacher zu debuggen, weil Stack-Trace kleiner wird, warum Timing für Sie wichtig ist? – GameDeveloper

Antwort

2

Besucher ist nur ein kompliziertes Gerüst, um Double-Dispatch auf Sprachen wie Java zu implementieren.

Wenn Sie mit Blattarten arbeiten, brauchen Sie keinen Doppelversand; Der Laufzeittyp ist zur Kompilierzeit bekannt. Eine Blattart direkt zu versenden, ist nicht nur eine Optimierung, sie ist mehr aus Prinzip.

Natürlich ist das Problem, in Zukunft kann ein Blatt-Typ ein Super-Typ werden. Mit dem heutigen Refactor-Tool in IDEs ist das kein großes Problem.

Es ist besser, ein einfaches Design für die Anforderung der Gegenwart zu machen, als ein komplexes Design für unbekannte zukünftige Anforderungen zu erstellen.


in Java 8 können wir doppelt Versand mit einer Syntax implementieren, die auf den realen Doppel Versand ganz in der Nähe ist

final DoubleDispatch<Root,Void> dd = new DoubleDispatch<>(); 

dd.register(X.class, x-> 
{ 
    do something with x; its compile time type is X 
    return null; 
}); 
dd.register(Y.class, y-> 
{ 
    do something with y; its compile time type is Y 
    return null; 
}); 
// etc 

... 
dd.invoke(something); 



// ---- 

public class DoubleDispatch<T, R> 
{ 
    public R invoke(T obj){...} 

    public <C extends T> void register(Class<C> type, Function<C,R> func){...} 
} 

auch sehen - Java Class.cast() and Overload

0

Könnte es Probleme verursachen? Schwer zu sagen. (Ich denke, es könnte zu Überraschungen führen, wenn die Grammatik sich ändert ...)

Aber ich denke, das eigentliche Problem ist, ob dies eine lohnende Optimierung ist. Konkret: Werden zwei Methodenaufrufe gespeichert, um einen großen Unterschied im Gesamtbild zu machen? Meine Intuition ist, dass es nicht so wäre.

Und ist die Leistung dieses Interpreters wirklich wichtig?

Und wenn ja, warum verwenden Sie das Besuchermuster im Interpreter? Sollten Sie nicht zu einem zwischenzeitlichen virtuellen Maschinencode kompilieren? Oder zu Bytecodes?

2

In diesem Fall wäre es kein Besuchermuster. Und es muss nicht sein, wenn es Ihren Anforderungen entspricht, Besucher werden oft missbraucht und führen zu einer Überarchitektur.

Sie verlieren jedoch potenzielle Vorteile. Sie können beispielsweise keinen Decorator oder Proxy für Ident erstellen und in der Methode accept etwas Zusätzliches ausführen, bevor Sie den Aufruf an das Decorated/Proxy-Objekt weiterleiten.