Laufzeit des eigenen prozesses ermitteln

Ich weiß, dass man vor und nach einem Codestück, das eine längere Zeit läuft, mit System.currentTimeMillis(); (oder auch System.nanoTime();) die aktuelle Systemzeit erfragen kann. Diese wird ja aber nicht nur für den eigenen Prozess verbraucht, sondern auch für andere, parallel laufende Programme.

Gibt es eine einfache Möglichkeit mit Bordmitteln, die wirklich verbrauchte Zeit für das eigenen Programm zu bekommen?

Hintergrund ist, dass ein Prozess nach einer bestimmten Zeit abgebrochen werden soll. Ist aber zu viel los auf dem betreffenden Rechner, erfolgt der Abbruch “zu früh”, weil der eigenen Prozess vielleicht nur ein Achtel dieser Zeit wirklich verwendet hat.

Mein erster Gedanke wäre, mal zu schauen, ob ExecuterService die Timeouts schon “richtig” berechnet. Wie startet ihr die Prozesse? Als “nackte” Threads?

Also ich sehe in der gesamten Java-Api ehrlich gesagt keinen einzigen Hinweis darauf, wie man an irgendeine Prozesslaufzeit kommt - sei es nun die Laufzeit von Start bis Abfrage oder die darin enthaltene effektive Prozessorzeit (also jene, in welcher der Thread tatsächlich etwas anderes tut außer warten). Muss man wohl tatsächlich alles selber implementieren, was glücklicherweise auch mit Bordmitteln möglich sein dürfte, wobei aber möglicherweise ein ganz neues Multithreading-Paket bei herauskäme (also was vollkommen anderes als java.util.concurrent).

Ah ok, so wichtig ist es dann auch nicht. Ich hab die Zeit hochgesetzt, so dass es passt, wenn 10 Prozesse den Rechner gemeinsam auslasten.

@Landei Die Prozesse werden in dem Fall über eine Oberfläche auf Knopfdruck gestartet, jeder Prozess läuft in seinem eigenen Thread, also nehme ich an, dass “nackte” Threads stimmt.

Wenn ich dir Doku richtig verstehe kannst du schon nanoTime nehmen, da es eben nicht die aktuelle Zeit zurück gibt sondern einen Zeitwert seit einem Zeitpunkt. Es steht auch da dass es nur zum messen geeignet ist.

Man wird aber dennoch die Thread-Lauf- bzw. Thread-Prozessorzeiten innerhalb der Run-Methode selbst zusammenzählen dürfen. Dafür würde ich sogar die Klasse Thread erweitern:

[code]public class Thread extends java.lang.Thread {
private long start, running, processor;

public Thread() {
	super();
}

public Thread(Runnable target, String name) {
	super(target, name);
}

public Thread(Runnable target) {
	super(target);
}

public Thread(String name) {
	super(name);
}

public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
	super(group, target, name, stackSize);
}

public Thread(ThreadGroup group, Runnable target, String name) {
	super(group, target, name);
}

public Thread(ThreadGroup group, Runnable target) {
	super(group, target);
}

public Thread(ThreadGroup group, String name) {
	super(group, name);
}

@Override
public void run() {
	long now = System.nanoTime();
	running = now - start;
	super.run();
	processor += (System.nanoTime() - now);
}

@Override
public synchronized void start() {
	start = System.nanoTime();
	super.start();
}

public long getRuntime() {
	return running;
}

public long getProcessorTime() {
	return processor;
}

}[/code]Aber jede Wette: So einfach wird es wahrscheinlich dann doch nicht… Irgendwas ist immer… z.B. wird wait oder sleep in Run-Methoden die Prozessorzeit verfälschen.

Das funktioniert so nicht. Ganz einfach deshalb, weil der Scheduler vom Betriebssystem die Run-Methode unterbrechen kann, selbst wenn keine blockierende Methode darin eingesetzt wird.

Das entspräche aber auch einem wait bzw. sleep - run wird nicht unterbrochen, sondern nur angehalten - und das verfälscht nur die Prozessorzeit. Bei einer Unterbrechung bzw. einem Abbruch sollte zumindest eine InterruptedException fliegen.

Du hast Recht, ich meinte angehalten und nicht unterbrochen.
Aber es geht ja nunmal auch darum festzustellen, wieviel Rechenzeit der Thread verbraucht hat. Und da müsste man Wartezeiten herausrechnen. Was ich betonen wollte ist, dass es egal ist, ob man eine blockierende Methode in der Run-Methode aufruft. Einzige Bedingung ist, dass die Methode lang genug läuft.

Mit anderen Worten, du brauchst diese verflixte Prozessorzeit, denn ein angehaltenes Programm läuft nun mal nicht.
Da diese mit Bordmitteln offensichtlich nicht zu ermitteln ist, stellt sich die Frage ob es überhaupt irgendwie geht - also ob der Scheduler des jeweiligen Betriebssystems diese überhaupt propagiert.

Ich kann mir durchaus vorstellen, dass sie es nicht tun, sonst hätte man einen entsprechenden Mechanismus sicher in Java bereits zur Verfügung gestellt.

Vermutlich geht das nicht plattformunabhängig.

Hy hab grad einen alten JavaSpecialists Newsletter gelesen und dabei bin ich durch einen Nebensatz auf eine Klasse gekommen die das Problem behebt.

ThreadMXBean

https://docs.oracle.com/javase/7/docs/api/java/lang/management/ThreadMXBean.html

Returns the total CPU time for the current thread in nanoseconds. The returned value is of nanoseconds precision but not necessarily nanoseconds accuracy. If the implementation distinguishes between user mode time and system mode time, the returned CPU time is the amount of time that the current thread has executed in user mode or system mode.

This is a convenient method for local management use and is equivalent to calling:

   getThreadCpuTime(Thread.currentThread().getId());
2 „Gefällt mir“

Oh man… Wer versteckt das denn in java.lang.management? Da schaut doch keiner nach. :smiley:

Huch! Wie unerwartet! Ja, da hätte ich so etwas auch nicht erwartet, und der Name klingt auch nach etwas ganz anderem. Aber schön, dass es offenbar doch geht.

Naja, eigentlich nicht so ganz. Nur nutzt man JMX wahrscheinlich viel zu selten, um da gleich dran zu denken.
Ich hatte heute in der VisualVM beim analysieren eines Tomcat auch mal die MXBeans angesehen - da sind noch ein paar andere interessante Beans mit Informationen über die Laufzeitumgebung freigegeben.

Es scheint ja nach

mehrere Methoden zu geben:

getCurrentThreadCpuTime() als Abkürzung für getThreadCpuTime(Thread.currentThread().getId());
und
getCurrentThreadUserTime() als Abkürzung für getThreadUserTime(Thread.currentThread().getId());
außerdem noch isThreadCpuTimeSupported(), isCurrentThreadCpuTimeSupported() und isThreadCpuTimeEnabled().

Ich nehme mal an, dass für meine Bedürfnisse die CPU-Time die gefragte ist.

Aber so ganz schlau werde ich daraus noch nicht:

package javathings;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

public class CpuTimeMeasuring {

    public static void main(String[] args) {
        printTime();
        sleep();
        printTime();
    }

    private static void printTime() {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        boolean isThreadCpuTimeSupported = bean.isThreadCpuTimeSupported();
        boolean isCurrentThreadCpuTimeSupported = bean.isCurrentThreadCpuTimeSupported();
        boolean isThreadCpuTimeEnabled = bean.isThreadCpuTimeEnabled();

        System.out.println("isThreadCpuTimeSupported        = " + isThreadCpuTimeSupported);
        System.out.println("isCurrentThreadCpuTimeSupported = " + isCurrentThreadCpuTimeSupported);
        System.out.println("isThreadCpuTimeEnabled          = " + isThreadCpuTimeEnabled);

        long id = Thread.currentThread().getId();
        long cpuTime1 = bean.getThreadCpuTime(id);
        long userTime1 = bean.getThreadUserTime(id);
        long cpuTime2 = bean.getCurrentThreadCpuTime();
        long userTime2 = bean.getCurrentThreadUserTime();

        System.out.println("\nZeiten:");
        System.out.println("    cpuTime1  : " + cpuTime1);
        System.out.println("    cpuTime2  : " + cpuTime2);
        System.out.println("    userTime1 : " + userTime1);
        System.out.println("    userTime2 : " + userTime2);
    }

    private static void sleep() {
        System.out.println("\nSchlafe 2 Sekunden...\n");
        try {
            Thread.sleep(2000);
        }
        catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

erzeugt die Ausgabe

isThreadCpuTimeSupported        = true
isCurrentThreadCpuTimeSupported = true
isThreadCpuTimeEnabled          = true

Zeiten:
	cpuTime1  : 109200700
	cpuTime2  : 109200700
	userTime1 : 62400400
	userTime2 : 62400400

Schlafe 2 Sekunden...

isThreadCpuTimeSupported        = true
isCurrentThreadCpuTimeSupported = true
isThreadCpuTimeEnabled          = true

Zeiten:
	cpuTime1  : 109200700
	cpuTime2  : 109200700
	userTime1 : 62400400
	userTime2 : 62400400

Daraus werde ich nicht so ganz schlau…

Ich werd da auch noch nicht 100%ig schlau draus, aber habe mal einen kurzen Test mit einer alternativen “Sleep”-Methode gemacht. Das sleep sieht so aus:

private static void activeSleep() {
    System.out.println("\nSchlafe 2 Sekunden (aktiv)...\n");
    long endTimeMillis = System.currentTimeMillis() + 2000;
    while (System.currentTimeMillis() < endTimeMillis) {}
}

Ergebnis bei mir:

isThreadCpuTimeSupported        = true
isCurrentThreadCpuTimeSupported = true
isThreadCpuTimeEnabled          = true

Zeiten:
    cpuTime1  : 109375000
    cpuTime2  : 109375000
    userTime1 : 78125000
    userTime2 : 78125000

Schlafe 2 Sekunden...

isThreadCpuTimeSupported        = true
isCurrentThreadCpuTimeSupported = true
isThreadCpuTimeEnabled          = true

Zeiten:
    cpuTime1  : 109375000
    cpuTime2  : 109375000
    userTime1 : 78125000
    userTime2 : 78125000

Schlafe 2 Sekunden (aktiv)...

isThreadCpuTimeSupported        = true
isCurrentThreadCpuTimeSupported = true
isThreadCpuTimeEnabled          = true

Zeiten:
    cpuTime1  : 2109375000
    cpuTime2  : 2109375000
    userTime1 : 2078125000
    userTime2 : 2078125000
1 „Gefällt mir“

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;


public class Test {

	public static class TestThread extends Thread {

		volatile int i = 0;
		volatile boolean run = true;

		public void run() {
			while (run) {
				i++     ;
			}

		}
	}

	public static void main(String[] args) throws InterruptedException {
		TestThread a = new TestThread();

		a.start();
		printTime(a.getId());
		Thread.sleep(2000);
		a.run = false;
		printTime(a.getId());


	}

	private static void printTime(long id) {
		ThreadMXBean bean = ManagementFactory.getThreadMXBean();
		boolean isThreadCpuTimeSupported = bean.isThreadCpuTimeSupported();
		boolean isCurrentThreadCpuTimeSupported = bean.isCurrentThreadCpuTimeSupported();
		boolean isThreadCpuTimeEnabled = bean.isThreadCpuTimeEnabled();

		System.out.println("isThreadCpuTimeSupported        = " + isThreadCpuTimeSupported);
		System.out.println("isCurrentThreadCpuTimeSupported = " + isCurrentThreadCpuTimeSupported);
		System.out.println("isThreadCpuTimeEnabled          = " + isThreadCpuTimeEnabled);


		long cpuTime1 = bean.getThreadCpuTime(id);
		long userTime1 = bean.getThreadUserTime(id);

		System.out.println("\nZeiten:");
		System.out.println("    cpuTime1  : " + cpuTime1);
		System.out.println("    userTime1 : " + userTime1);

	}
	
}
	

Bei sleep wird ja keine CPU time verbraucht daher zählt da auch nichts weiter

wenn der Thread aber tatsächlich arbeitet dann zählt er auch weiter wie in deiner sleep aktiv oder in meiner
run methode.

1 „Gefällt mir“

@AmunRa: aber die Walltime sollte sich ändern. Ist das nicht mit der “ThreadUserTime” gemeint?

Kann es sein, dass die Bean (und damit die Zeiten) immer neu initialisiert wird, wenn man sich eine Instanz aus der Factory holt? Werde ich, gleub ich, mal ausprobieren.