Wie misst man die Ausführungszeit eines Versprechens?

Lesezeit: 5 Minuten

Benutzer-Avatar
Vivien Adnot

Ich versuche, eine Funktion zu schreiben, die die Ausführungszeit einer anderen Funktion misst:

export class Profiler {
    public measureSyncFunc(fn: () => any): Promise<number> {
        return new Promise<number>((resolve, reject) => {
            let elapsed = 0;

            let intervalId = window.setInterval(() => {
                elapsed += 1; // this is never called
            }, 1);

            this.execFunc(fn)
                .then((result: any) => {
                    window.clearInterval(intervalId);
                    resolve(elapsed);
                });
        });
    }

    private execFunc(fn: () => any): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            resolve(fn());
        });
    }
}

Dann benutze ich es so:

let array = generateRandomArray(100000);

instance.measureSyncFunc(bubbleSort(array))
    .then((elapsed: number) => {
        console.log(`end session: ${elapsed} seconds`);
        resolve();
    });

Die BubbleSort-Funktion ist synchron und es dauert einige Sekunden, bis sie abgeschlossen ist. Siehe Code hier:

Das Ergebnis in der Konsole ist “Ende Sitzung: 0 Sekunden”, da der Intervall-Callback nie aufgerufen wird.

Weißt du, wie ich es anrufen kann? Vielen Dank Jungs!

  • =+ ist ein ungültiger Operator

    – charlietfl

    24. Mai 2017 um 12:12 Uhr

  • Hinweis instance.measureSyncFunc(bubbleSort(array)) Sie passieren die Ergebnis des Anrufs bubbleSort zu Ihrem measureSyncFuncaber measureSyncFunc erwartet eine Funktion als Argument

    – Jaromanda X

    24. Mai 2017 um 12:12 Uhr

  • setInterval von 1 ms – ein so kleines Intervall ist eigentlich nicht möglich – haben Sie die heutzutage in den meisten Browsern verfügbaren Profiling-Tools in Betracht gezogen? performance.now() oder console.time('xxx') / console.timeEnd('xxx')

    – Jaromanda X

    24. Mai 2017 um 12:14 Uhr


  • @charlietfl invalid operator – eigentlich würde das dazu führen elapsed == 1dauerhaft :p

    – Jaromanda X

    24. Mai 2017 um 12:17 Uhr

Benutzer-Avatar
toskv

Wenn die Funktionen, die Sie messen möchten, immer synchron sind, müssen Sie wirklich keine Versprechungen machen.

Da die Funktion, die Sie testen möchten, Parameter benötigt, packen Sie sie am besten in eine Pfeilfunktion, um sie mit einem anderen Kontext aufrufen zu können und ihre Parameter nicht selbst verwalten zu müssen.

Etwas Einfaches wie dieses wird gut tun.

function measure(fn: () => void): number {
    let start = performance.now();
    fn();
    return performance.now() - start;
}

function longRunningFunction(n: number) {
    for (let i = 0; i < n; i++) {
        console.log(i);
    }
}

let duration = measure(() => {
    longRunningFunction(100);
});

console.log(`took ${duration} ms`);

Wenn Sie die Zeit messen möchten, die eine asynchrone Funktion (wenn sie ein Versprechen zurückgibt) zum Auflösen benötigt, können Sie den Code einfach in etwa so ändern:

function measurePromise(fn: () => Promise<any>): Promise<number> {
    let onPromiseDone = () => performance.now() - start;

    let start = performance.now();
    return fn().then(onPromiseDone, onPromiseDone);
}

function longPromise(delay: number) {
    return new Promise<string>((resolve) => {
        setTimeout(() => {
            resolve('Done');
        }, delay);
    });
}

measurePromise(() => longPromise(300))
    .then((duration) => {
        console.log(`promise took ${duration} ms`);
    });

Hinweis: Diese Lösung verwendet den ES6 Versprechenwenn Sie etwas anderes verwenden, müssen Sie es möglicherweise anpassen, aber die Logik sollte dieselbe sein.

Sie können beide Beispiele im Playground sehen hier.

  • Und wie @JaromandaX in einem Kommentar zu der Frage betonte, besteht die vielleicht wichtigste Änderung darin, die nicht zu bestehen Ergebnis der Langzeitfunktion in die Timing-Funktion.

    – TripeHound

    24. Mai 2017 um 12:55 Uhr

  • in der Tat bedeutet dies zumindest für Sync-Funktionen, dass die gesamte Ausführung bereits abgeschlossen ist. Ich werde das als Bemerkung in die Antwort einfügen, wenn es Ihnen nichts ausmacht. 🙂

    – toskv

    24. Mai 2017 um 12:56 Uhr

  • Auf jeden Fall (aber der Verdienst geht an @JaromandaX). Wie gut/schlecht der Rest des OP-Codes auch war, er hätte ohne das Lambda-Konstrukt (oder Äquivalent) unmöglich funktionieren können.

    – TripeHound

    24. Mai 2017 um 13:40 Uhr

  • normale Funktion würde es auch tun, aber Pfeilfunktionen sind in diesem Fall flexibler.

    – toskv

    24. Mai 2017 um 13:41 Uhr

  • Es sollte beachtet werden, dass dieser Ansatz ist nicht zuverlässig wenn mehrere Promises parallel ausgeführt werden – wenn die Promises eine gewisse Menge an Blockierungsarbeit leisten, werden die Ergebnisse mehr oder weniger zufällig sein. Ich habe alle Ansätze in diesem Thread ausprobiert (Stand 13. April 22) und keiner von ihnen funktioniert. Ich glaube nicht, dass es einen zuverlässigen Weg gibt, Versprechen zeitlich festzulegen. Wenn Sie x Promises parallel ausführen, hängt das, was Sie am Ende messen, von der internen Planung ab, und Sie messen letztendlich die Zeit, die das langsamste Promise für alle Promises benötigt.

    – mindplay.dk

    13. April um 9:29 Uhr

Nicht verwenden setInterval um Millisekunden zu zählen (es ist ungenau, verzögert, driftet und hat ein Mindestintervall von etwa 4 ms). Holen Sie sich einfach zwei Zeitstempel vor und nach der Ausführung.

function measureASyncFunc(fn: () => Promise<any>): Promise<number> {
    const start = Date.now();
    return fn.catch(() => {}).then(() => {
        const end = Date.now();
        const elapsed = end-start;
        return elapsed;
    });
}

Für höhere Genauigkeit ersetzen Date.now durch performance.now.

Schau mal rein timeFnPromise und das verwandte Testfälle.

  • Die Zielfunktion wird umschlossen und ausgeführt, wenn die umschlossene Funktion aufgerufen wird
  • Hängt Erfüllungs-/Ablehnungs-Handler an das zugrunde liegende Promise an, das den Rückgabewert der Zielfunktionen als „ret“ und die verstrichene Zeit als „elapsedTime“ zurückgibt.
  • unterstützt Argumente, indem es sie an die Zielfunktion weiterleitet

Beispielverwendung:

const wrappedFn = timeFnPromise(aFunctionThatReturnsAPromise)

wrappedFn()
.then((values)=>{
  const {ret, elapsedTime} = values
  console.log(`ret:[${ret}] elapsedTime:[${elapsedTime}]`)
})

Auch über das NPM-Modul verfügbar jschest.

Hier ist eine einfache Wrapper-Funktion, die ich geschrieben habe. Es gibt ein Promise (über das Schlüsselwort async) zurück, sodass Sie es einfach mit Ihrem Promise aufrufen können. Ich habe den Zeitwert als Eigenschaft zur Antwort hinzugefügt. Wenn Sie diesen Wert nicht in der Antwort haben können, müssen Sie ihn anschließend entfernen.

const stopwatchWrapper = async (promise) => {
  const startTime = Date.now()
  const resp = await promise
  resp.executionTime = Date.now() - startTime
  return resp
}

const axiosPromise = stopwatchWrapper(axios(reqSelected))
const response = await axiosPromise
console.log(response.executionTime)

Es wäre gut klarzustellen, dass die vorgeschlagenen Ansätze von toskv nur mit der Auflösung eines einzigen Versprechens funktionieren. Wenn wir Promise.all() verwenden wollen, ist das zurückgegebene Zeitergebnis falsch.

Hier ist ein Beispiel mit dem von toskv entwickelten Code, aber mit Promise.all()

Messen mit Promise.all()

Wenn jemand die Zeit messen muss, die benötigt wird, um jedes der mit Promise.all() ausgeführten Versprechen auszuführen, besteht der Ansatz darin, die Interzeptoren zu verwenden und dort die Zeitmessungen durchzuführen

1016030cookie-checkWie misst man die Ausführungszeit eines Versprechens?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy