Skip to content
Alle Beiträge

Optimierung von Serverless Funktionen – Performance und Kosten

Serverless speed

Motivation

Serverless Funktionen sind hervorragend geeignet, um schnelle, stark skalierende Programme auszuführen. In den meisten Fällen wird die Funktion pro Ausführung gestartet. Dabei wird üblicherweise anhand ausgeführter Rechenzeit und benötigtem Arbeitsspeicher abgerechnet.

Um dem Anspruch an die Skalierbarkeit und Neustart pro Ausführung optimal gerecht zu werden, muss also die Startzeit möglichst gering gehalten werden. Wenn die Funktion häufig verwendet wird, ist auch die Optimierung der Kosten und damit der Laufzeitperformance interessant.

Im Folgenden betrachten wir ein bewährtes Vorgehen und geben einige konkrete, praktische Tipps, um so eine Optimierung durchzuführen.

Anforderungen als Grundlage der Optimierung

Bevor optimiert werden kann, müssen die Anforderungen an die Anwendung spezifiziert werden. Optimierung muss zielgerichtet erfolgen. Eine Backend-for-Frontend-Anwendung beispielsweise muss in Hinblick auf die Round-Trip-Time der Endnutzer-Anfrage ausgelegt werden, da hier die User-Experience betroffen ist.

Ebenso kann es erforderlich sein, die Kosten pro Ausführung zu verbessern. Welche Anforderung hier wichtiger ist, muss aus den Qualitätsanforderungen entschieden werden. Die Verbesserung der Round-Trip-Time kann beispielsweise möglicherweise durch asynchrone Verarbeitung erreicht werden, während eine Erhöhung des Arbeitsspeichers für die Funktion hier meist wenig Einfluss hat.

Umsetzung

Nachdem das Ziel und die Anforderungen an die Optimierung festgestellt wurden, erfolgt die Umsetzung der Optimierung in mehreren Schritten. Zuerst müssen die zu verbessernden Kennzahlen messbar gemacht werden. Dann werden die Optimierungsschritte selbst unternommen und die Auswirkungen erneut gemessen. Iterativ kann man sich so dem Optimierungsziel nähern.

Messungen

Die Durchführung der Messungen ist abhängig vom verwendeten Cloud Service Provider. Am Beispiel von AWS können die Kosten im Cost Manager eingesehen werden, wobei die Trennung auf einzelne Funktionen kaum möglich ist.

Deshalb bietet sich zusätzlich die Verwendung von Cloud Watch an, wo mittels geeigneter Abfragen die Kosten ermittelt werden können. Für die aktuellen Preise wäre eine folgende Abfrage möglich, die dann direkt die Kosten im betrachteten Zeitraum ausgibt:

parse @message /Duration:\s*(?<@duration_ms>\d+\.\d+)\s*ms\s*Billed\s*Duration:\s*(?<@billed_duration_ms>\d+)\s*ms\s*Memory\s*Size:\s*(?<@memory_size_mb>\d+)\s*MB/| filter @message like /REPORT RequestId/| stats sum(@billed_duration_ms * @memory_size_mb * 1.6279296875e-11 + 2.0e-7) as @cost_dollars_total


In der Query werden zwei Konstanten verwendet (2):
* 1,627607421875e-11: Kosten pro ms und MB (0,0000166667 USD für jede GB-Sekunde / 1024 (GB zu MB) / 1000 (Sekunde zu ms))
* 2.0e-7: Kosten pro Millionen Ausführungen

Ein sehr nützliches Tool zur Messung von Performance und Kosten ist in AWS auch das sogenannte Power Tuning Tool (1).

Das Tool ermöglicht es, die Funktion mit verschiedenen Speicherkonfigurationen mehrfach auszuführen und gibt die Laufzeit und Kosten unter diesen Konfigurationen an. So lässt sich sehr einfach eine optimale Konfiguration gemäß den eigenen Anforderungen finden.

Das Ergebnis dieser Messung wird wie in Abbildung 1 dargestellt. In diesem Beispiel sieht man, dass die Kosten mit steigendem Speicher natürlich steigen, dort die Durchlaufzeit aber nicht nennenswert verbessert wird. Hier wird also kaum Mehrwert von mehr Speicherzuweisung erzielt. In anderen Anwendungen kann das Ergebnis völlig anders ausfallen.

Abbildung_1_Ergebnis_des_Power_Tuning_Tools2Abbildung 1: Ergebnis des Power Tuning Tools
 
Optimierung vornehmen

Hat man präzise Messungen erhalten, müssen nun die Optimierungen in Bezug auf die eigenen Anforderungen durchgeführt werden. Soll die Performance gesteigert werden, ist die Erhöhung des zugewiesenen Arbeitsspeichers meist das einfachste Mittel. Hier muss dann aber (z.B. mittels Power Tuning Tool) verifiziert werden, dass wirklich die gewünschte Verbesserung erzielt wird.

Ein anderes Mittel ist die Optimierung der Ausführungsumgebung. Die Wahl der Programmiersprache hat enormen Einfluss auf die Startzeiten der Funktion. Sollen sogenannte Cold Starts ganz vermieden werden, kann auch „Provisioned Concurrency“ gebucht werden, wo immer mehrere Funktions-Instanzen vorab gestartet und bereitgehalten werden. Das geht aber auch mit deutlich erhöhten Kosten einher (3).

Abbildung_2_Lambda_Ausführungsschritte2
Abbildung 2: Lambda Ausführungsschritte

 

Zuletzt muss immer durch erneute Messungen sichergestellt werden, dass das Optimierungsziel erreicht wurde. Meist sind mehrere Iterationen dieses Vorgehens sinnvoll, um sich dem eigenen Ziel stückweise anzunähern.


Fazit

Man sieht also, dass die Optimierung von Funktionen nicht trivial ist und immer eine Abwägung widersprüchlicher Ziele beinhaltet. Die Steigerung der Leistung steht meist höheren Kosten gegenüber.

Die Bestimmung der eigenen Ziele muss also immer zuerst erfolgen und dann auch kontinuierlich überprüft werden.




Quellen:
(1) https://github.com/alexcasalboni/aws-lambda-power-tuning
(2) https://aws.amazon.com/de/lambda/pricing/
(3) https://docs.aws.amazon.com/lambda/latest/operatorguide/execution-environments.html#cold-starts

Weitere Beiträge