パフォーマンスチューニング
SnapStart の利点を最大限に活用するには、ランタイムに対して次のコード最適化の推奨事項を検討してください。
注記
SnapStart は、大規模な関数呼び出しで使用すると最も効果的です。頻繁に呼び出されない関数では、パフォーマンスが同じように向上されない場合があります。
SnapStart の利点を最大限に活用するには、起動時のレイテンシーの原因となる依存関係の事前ロードとリソースの初期化は、関数ハンドラーではなく初期化コードで行うことをお勧めします。そうすることで、高負荷のクラスローディングに関連するレイテンシーが呼び出しパスから排除され、SnapStart での起動パフォーマンスが最適化されます。
初期化中に依存関係やリソースを事前ロードできない場合は、ダミー呼び出しを使用して事前ロードすることをお勧めします。これを実行するには、AWS Labs GitHub リポジトリの pet-store 関数
private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class);
// Use the onStartup method of the handler to register the custom filter
handler.onStartup(servletContext -> {
FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter", CognitoIdentityFilter.class);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
});
// Send a fake Amazon API Gateway request to the handler to load classes ahead of time
ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity();
identity.setApiKey("foo");
identity.setAccountId("foo");
identity.setAccessKey("foo");
AwsProxyRequestContext reqCtx = new AwsProxyRequestContext();
reqCtx.setPath("/pets");
reqCtx.setStage("default");
reqCtx.setAuthorizer(null);
reqCtx.setIdentity(identity);
AwsProxyRequest req = new AwsProxyRequest();
req.setHttpMethod("GET");
req.setPath("/pets");
req.setBody("");
req.setRequestContext(reqCtx);
Context ctx = new TestContext();
handler.proxy(req, ctx);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring framework", e);
}
}
SnapStart の利点を最大限に活用するには、Python 関数内の効率的なコード整理とリソース管理に焦点を当てます。一般的なガイドラインとして、高負荷の計算タスクは初期化フェーズで実行してください。このアプローチにより、時間のかかるオペレーションが呼び出しパスから外れ、全体的な関数パフォーマンスが向上します。この戦略を効果的に実装するために、以下のベストプラクティスをお勧めします。
-
関数ハンドラーの外部で依存関係をインポートします。
-
ハンドラーの外部で
boto3
インスタンスを作成します。 -
ハンドラーが呼び出される前に静的リソースや設定を初期化します。
-
外部ファイルのダウンロード、Django などのフレームワークの事前ロード、機械学習モデルのロードなど、リソースを大量に消費するタスクには、スナップショット前のランタイムフックの使用を検討してください。
例 — SnapStart 用に Python 関数を最適化する
# Import all dependencies outside of Lambda handler
from snapshot_restore_py import register_before_snapshot
import boto3
import pandas
import pydantic
# Create S3 and SSM clients outside of Lambda handler
s3_client = boto3.client("s3")
# Register the function to be called before snapshot
@register_before_snapshot
def download_llm_models():
# Download an object from S3 and save to tmp
# This files will persist in this snapshot
with open('/tmp/FILE_NAME', 'wb') as f:
s3_client.download_fileobj('amzn-s3-demo-bucket', 'OBJECT_NAME', f)
...
def lambda_handler(event, context):
...
ジャストインタイム (JIT) のコンパイルおよびアセンブリロードの時間を短縮するには、RegisterBeforeCheckpoint
ランタイムフックから関数ハンドラーを呼び出すことを検討してください。.NET 階層型コンパイルの仕組みにより、次の例に示すように、ハンドラーを複数回呼び出すことで最適な結果が得られます。
重要
ダミー関数の呼び出しによって、ビジネストランザクションの開始など、意図しない副作用が生じないようにしてください。
public class Function
{
public Function()
{
Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(FunctionWarmup);
}
// Warmup method that calls the function handler before snapshot to warm up the .NET code and runtime.
// This speeds up future cold starts after restoring from a snapshot.
private async ValueTask FunctionWarmup()
{
var request = new APIGatewayProxyRequest
{
Path = "/heathcheck",
HttpMethod = "GET"
};
for (var i = 0; i < 10; i++)
{
await FunctionHandler(request, null);
}
}
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
{
//
// Process HTTP request
//
var response = new APIGatewayProxyResponse
{
StatusCode = 200
};
return await Task.FromResult(response);
}
}
ネットワークのベストプラクティス
Lambda がスナップショットから関数を再開するときは、関数が初期化フェーズ中に確立する接続の状態が保証されません。ほとんどの場合、AWS SDK が確立するネットワーク接続は、自動的に再開されます。これ以外の接続については、以下のベストプラクティスが推奨されます。
ネットワーク接続を再確立する
関数がスナップショットから再開されるときは、常にネットワーク接続を再確立してください。ネットワーク接続を再確立は、関数ハンドラーで実行することをお勧めします。代わりに、復元後のランタイムフックを使用することもできます。
ホスト名を一意の実行環境識別子として使用しない
実行環境をアプリケーション内の一意のノードやコンテナとして特定するために hostname
を使用することはお勧めしません。SnapStart では、単一のスナップショットが複数の実行環境の初期状態として使用されます。すべての実行環境で、InetAddress.getLocalHost()
(Java)、socket.gethostname()
(Python)、Dns.GetHostName()
(.NET) に対して同じ hostname
値が返されます。一意の実行環境アイデンティティ、または hostname
値を必要とするアプリケーションでは、関数ハンドラーで一意の ID を生成することをお勧めします。または、復元後のランタイムフックを使用して一意の ID を生成してから、その一意の ID を実行環境の識別子として使用してください。
接続を固定送信元ポートにバインドしない
ネットワーク接続を固定ソースポートにバインドしないことをお勧めします。接続は、関数がスナップショットから再開されるときに再確立され、固定送信元ポートにバインドされたネットワーク接続は失敗する可能性があります。
Java DNS キャッシュを使用しない
Lambda 関数は、既に DNS レスポンスをキャッシュしています。SnapStart で別の DNS キャッシュを使用すると、関数がスナップショットから再開されるときに接続タイムアウトが発生する可能性があります。
java.util.logging.Logger
クラスは JVM DNS キャッシュを間接的に有効にできます。デフォルト設定を上書きするには、 を初期化する前に networkaddress.cache.ttllogger
。例:
public class MyHandler {
// first set TTL property
static{
java.security.Security.setProperty("networkaddress.cache.ttl" , "0");
}
// then instantiate logger
var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class);
}
Java 11 ランタイムで UnknownHostException
障害を防ぐには、networkaddress.cache.negative.ttl
を 0 に設定することをお勧めします。Java 17 以降のランタイムでは、このステップは必要ありません。このプロパティは、 AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0
環境変数を使用して Lambda 関数に設定できます。
JVM DNS キャッシュを無効にしても、Lambda のマネージド DNS キャッシュは無効になりません。