PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Zeitmessung für Java-Methoden


Shink
2007-08-29, 11:20:07
Hallo!

Was wäre denn die einfachste Möglichkeit, die Ausführungsdauer einer Methode oder einer ihrer abgeleiteten Methoden zu messen?

Also z.B.:

abstract class LoggedClass {
static void accessLasted(String methodName, long millis) {
// writeToDb
}

abstract BigDecimal greatestCommonDivisor(BigDecimal value1, BigDecimal value2);
}


Ich will, dass von allen Klassen, die greatestCommonDivisor() überschreiben, die Zeit gemessen und mit accessLasted("greatestCommonDivisor", time) geloggt wird.

Das wär natürlich eine AOP-Geschichte. Fällt jemandem eine POJO-Variante ein (ich will das natürlich nicht für jede abgeleitete Klasse neu implementieren, auch wenns nur zwei Methodenaufrufe sind) oder was wär die schnellste, einfachste und "libraryärmste" Methode das mit AOP zu machen?

Abnaxos
2007-08-29, 11:31:43
Eine einfach Möglichkeit sehe ich da nicht, da musst du schon mit AspectJ, AspectWorkz oder JBoss AOP auffahren. Für diesen einfachen Fall könntest du auch einen eigenen ClassLoader oder java.lang.instrumentation verwenden, in Verbindung mit einer Bytecode-Library wie Javassist (http://www.csg.is.titech.ac.jp/~chiba/javassist/) oder ASM (http://asm.objectweb.org/). Javassist ist sehr leichtgewichtig, hier reicht ein einziges zusätzliches Jar-File im Classpath. Mit ASM habe ich keine Erfahrung, das dürfte aber ähnlich aussehen.

Monger
2007-08-29, 11:41:24
Mal ganz dämlich gefragt: sind Annotations vererbbar?

Wenn ja, lässt sich vielleicht für die Testdauer da irgendwas an die Klasse anhängen, was dann von außen auswertbar ist.

Das ist jetzt nur ein völliger Schuss ins blaue, ich hab mit den Annotations buchstäblich null Erfahrung, aber sie sollen ja nunmal ein Stück weit in Richtung AOP gehen.

Shink
2007-08-29, 11:44:26
Danke, das ging ja mal schnell... Macht mich nicht wirklich glücklich diese Antwort aber das fürchtete ich bereits.

Abnaxos
2007-08-29, 11:52:24
Mal ganz dämlich gefragt: sind Annotations vererbbar?

Ja.

Wenn ja, lässt sich vielleicht für die Testdauer da irgendwas an die Klasse anhängen, was dann von außen auswertbar ist.

Nicht ohne Handarbeit. Annotations sind reine Meta-Informationen, ohne irgend etwas, was die Annotations auswertet, bewirken sie nichts -- womit wir wieder zurück beim eigenen ClassLoader/java.lang.instrumentation und Bytecode-Manipulation wären. Über das APT (Annotation Processing Tool) bzw. seit JDK 6 einfach mit javac und dem Package javax.annotation.processing und javax.lang.model ff. könnte man das auch in die Compile-Time verlagern, aber um die Manipulation des Bytecodes kommt man nicht herum.

Das ist jetzt nur ein völliger Schuss ins blaue, ich hab mit den Annotations buchstäblich null Erfahrung, aber sie sollen ja nunmal ein Stück weit in Richtung AOP gehen.

Nein, Annotations haben mit AOP nichts zu tun, sie können aber durchaus auch dafür verwendet werden. Es kommt halt darauf an, was du mit ihnen anstellst ... :)

Monger
2007-08-29, 12:53:39
Annotations sind reine Meta-Informationen, ohne irgend etwas, was die Annotations auswertet, bewirken sie nichts

Das ist bei .NET anders, oder?
Dort wird direkt Code in den Attributen (wie sie dort heißen) ausgeführt, z.B. um den nachfolgenden Code in einen eigenen Thread zu wuchten, o.ä.

Da habe ich auch mal irgendwo ein Tutorial gesehen, was dann halt z.B. die Startzeit der Methode in irgendein Log reinschreibt. Und imho werden die standardmäßig ausgeführt, auch ganz ohne angeflanschte Agenten.
So ungefähr hatte ich mir das auch bei Java vorgestellt.

Shink
2007-08-29, 14:08:53
Ich werde es übrigens vermutlich mit Spring verwenden (dort definiere ich die aspektorientierten Dinge in einer zusätzlichen XML)

ethrandil
2007-08-29, 14:12:07
Also - wenn du das nur zu Testzwecken brauchst hilft natürlich auch ein Profiler. Nicht dass alle die sowas brauchen und den thread lesen nun nach Aspektorientierung googeln ;)

- eth

Shink
2007-08-29, 14:40:14
Für Testzwecke wär mir das relativ egal...
Geht um augelieferte Serversoftware, deren Performance wir gerne auf lange Zeit hin protokollieren wollen, um eventuelle Probleme erklären bzw. rechtfertigen zu können.

Bietchiebatchie
2007-08-29, 15:36:50
Hallo!

Was wäre denn die einfachste Möglichkeit, die Ausführungsdauer einer Methode oder einer ihrer abgeleiteten Methoden zu messen?

...

Ich will, dass von allen Klassen, die greatestCommonDivisor() überschreiben, die Zeit gemessen und mit accessLasted("greatestCommonDivisor", time) geloggt wird.

...




also wenn ich dich nicht gerade falsch verstanden habe geht das eigentlich relativ easy: (mehr oder weniger pseudocode)


abstract class LoggedClass
{
static void accessLasted(String methodeName, long millis)
{
// writeToDb
}

public BigDecimal GetGCD(BigDecimal value1, BigDecimal value2);
{
DateTime beforeRun = DateTime.GetActual();
BigDecimal result = greatestCommonDivisor(value1, value2);
long seconds = DateTime.GetActual() - beforeRun;
accessLasted("GCD", seconds);
return result;
}

protected abstract BigDecimal greatestCommonDivisor(BigDecimal value1, BigDecimal value2);

}


Du implementierst also nur die protected abstract-Methode; nach außen hin kannst du jede Klasse allerdings nur mit GetGDC ansprechen.

del_4901
2007-08-29, 15:49:17
also wenn ich dich nicht gerade falsch verstanden habe geht das eigentlich relativ easy: (mehr oder weniger pseudocode)


abstract class LoggedClass
{
static void accessLasted(String methodeName, long millis)
{
// writeToDb
}

public BigDecimal GetGCD(BigDecimal value1, BigDecimal value2);
{
long beforeRun = System.nanoTime();
BigDecimal result = greatestCommonDivisor(value1, value2);
long seconds = System.nanoTime() - beforeRun;
accessLasted("GCD", seconds);
return result;
}

protected abstract BigDecimal greatestCommonDivisor(BigDecimal value1, BigDecimal value2);

}


Du implementierst also nur die protected abstract-Methode; nach außen hin kannst du jede Klasse allerdings nur mit GetGDC ansprechen.

Naja wenn dann macht man das mit nanoTime. Aber ich glaube er wollte den Code nicht so "verschmutzen" und wollte deswegen auf AOP setzen. Aber selbst nanoTime könnte zu ungenau sein. Und der Zugriff auf <rdtsc> ist unter Java nicht ganz trivial, und auch nicht immer möglich.

EgonOlsen
2007-08-29, 16:53:06
Man könnte was mit dynamischen Proxies machen. Dazu müsste jede zu testende Instanz in einen Proxy packen. Ich würde es im Leben nicht machen für diese Sache, aber exemplarisch etwa so:


package test;

import java.math.*;

interface LoggedClass {
void accessLasted(String methodName,long millis);

BigDecimal greatestCommonDivisor(BigDecimal value1,BigDecimal value2);
}



package test;

import java.math.*;

public abstract class AbstractLoggedClass implements LoggedClass {
public void accessLasted(String methodName, long millis) {
System.out.println(methodName+"/"+millis);
}

public abstract BigDecimal greatestCommonDivisor(BigDecimal value1, BigDecimal value2);
}



package test;

import java.lang.reflect.*;

public class LoggedClassProxy implements InvocationHandler {

private LoggedClass lcn=null;
private LoggedClass obj=null;

public static LoggedClass create(LoggedClass lc) {
LoggedClassProxy lcp=new LoggedClassProxy(lc);
return lcp.lcn;
}

private LoggedClassProxy(LoggedClass lc) {
obj=lc;
lcn = (LoggedClass) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start=System.nanoTime();
Object result = method.invoke(obj, args);
if (method.getName().equals("greatestCommonDivisor")) {
long end=System.nanoTime()-start;
obj.accessLasted(method.getName(), end);
}
return result;
}
}



package test;

import java.math.*;

public class LoggedClassTest {
public static void main(String[] args) {
LoggedClass test1 = LoggedClassProxy.create(new Test1());
LoggedClass test2 = LoggedClassProxy.create(new Test2());

test1.greatestCommonDivisor(null,null);
test2.greatestCommonDivisor(null,null);
}
}

class Test1 extends AbstractLoggedClass implements LoggedClass {
public BigDecimal greatestCommonDivisor(BigDecimal value1,BigDecimal value2) {
try {
Thread.sleep(1000);
}
catch (Exception e) {}
return new BigDecimal(0);
}
}

class Test2 extends AbstractLoggedClass implements LoggedClass {
public BigDecimal greatestCommonDivisor(BigDecimal value1,BigDecimal value2) {
try {
Thread.sleep(2000);
}
catch (Exception e) {}
return new BigDecimal(0);
}
}

Shink
2007-08-29, 20:14:48
Du implementierst also nur die protected abstract-Methode; nach außen hin kannst du jede Klasse allerdings nur mit GetGDC ansprechen.
Ja, das wär natürlich machbar. Allerdings Hab ich alle 60 Methoden dieser Klasse bereits 5 Mal von abgeleiteten Klassen überschrieben und diverse externe Libraries greifen darauf zu...
@AlphaTier: Es handelt sich leider um Zugriffszeiten im Sekundenbereich, also brauch ich keine Nanotime. Darf übrigens auch nur Java 1.4 verwenden...

@EgonOlsen: Danke, das ginge natürlich.

Shink
2007-08-30, 17:33:02
Ich werde es übrigens vermutlich mit Spring verwenden (dort definiere ich die aspektorientierten Dinge in einer zusätzlichen XML)
Bin gerade soetwas von kläglich gescheitert. Wenn das mal einer probiert: Denkt nicht, das kann man so einfach an ein bestehendes Projekt dranflanschen.

del_4901
2007-08-30, 17:36:43
Bin gerade soetwas von kläglich gescheitert. Wenn das mal einer probiert: Denkt nicht, das kann man so einfach an ein bestehendes Projekt dranflanschen.
Tja, da würde ich mal euren Software-Architekten steinigen ... sag's nicht, ihr habt gar Keinen ... (kenne dieses Problem nur zu gut, seiddem ich das Sagen hatte, läuft der Laden)