Java running on AWS Lambda is slow the first time, but I searched for a way to make it faster. I arrived at the contents of the reference site at the end and actually tried it, so it is a record.
Latency information was obtained by X-Ray.
Just put the file to S3
S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));
First, try it normally.
package helloworld;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
public class TestTarget0429 implements RequestHandler<Object, Object> {
public Object handleRequest(final Object input, final Context context) {
String ENV_BUCKET = System.getenv("BUCKET");
S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
return null;
}
}
Number of times | Latency(ms) | Processing content |
---|---|---|
1 | 6200 | |
2 | 422 | |
3 | 217 | |
4 | 210 | |
5 | 315 |
Only the first time is late, so-called cold start is late. Just creating one file in S3 is 6 seconds slow, isn't it?
So how about using the Provisioned Concurrency
that appeared at the end of last year?
https://aws.amazon.com/jp/blogs/news/new-provisioned-concurrency-for-lambda-functions/
The source code is the same as Verification 1.
Number of times | Latency(ms) | Processing content |
---|---|---|
1 | 5500 | |
2 | 266 | |
3 | 274 | |
4 | 402 | |
5 | 304 |
** Isn't the first time still late? .. ** ** It costs $ 14.42 a month just to provision Concurrency 1, but that's not enough. .. ..
So, after that, disable Provisioned Concurrency and continue verification
In order to find out the cause of the slowness at the first time, I tried to separate the processing at the first startup of Lambda and the second startup.
Create a static count
variable and try a haste return only the first call.
if (count == 1) {
count++;
return null;
}
package helloworld;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
public class TestTarget0429 implements RequestHandler<Object, Object> {
private static int count = 1;
public Object handleRequest(final Object input, final Context context) {
if (count == 1) {
count++;
return null;
}
String ENV_BUCKET = System.getenv("BUCKET");
S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
return null;
}
}
Number of times | Latency | Processing content |
---|---|---|
1 | 625ms | Initialization process only |
2 | 5600ms | S3 PUT (1st time) |
3 | 393ms | S3 PUT (2nd time) |
4 | 401ms | S3 PUT (3rd time) |
5 | 311ms | S3 PUT (4th time) |
It turns out that the Initialization process is not slow. It seems that S3 PUT (first time) is taking a long time.
Let's make the part that creates S3Client static.
private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
package helloworld;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
public class TestTarget0429 implements RequestHandler<Object, Object> {
private static int count = 1;
private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
public Object handleRequest(final Object input, final Context context) {
if (count == 1) {
count++;
return null;
}
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
return null;
}
}
Number of times | Latency | Processing content |
---|---|---|
1 | 2400ms | Initialization process and S3Client instance generation |
2 | 2200ms | S3 PUT (1st time) |
3 | 43ms | S3 PUT (2nd time) |
4 | 46ms | S3 PUT (3rd time) |
5 | 78ms | S3 PUT (4th time) |
Oh! It took a little longer to process the first time, and the second time was a little faster. It's getting faster after the third time, but is this also having any effect?
I found that processing with static makes it faster. Let's create a dummy file with the static initializer once.
static{
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("dummy.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
}
package helloworld;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
public class TestTarget0429 implements RequestHandler<Object, Object> {
private static int count = 1;
private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
static{
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("dummy.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
}
public Object handleRequest(final Object input, final Context context) {
if (count == 1) {
count++;
return null;
}
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
return null;
}
}
Number of times | Latency | Processing content |
---|---|---|
1 | 4000ms | S3 PUT (first time) dummy file by Initialization processing and static method |
2 | 42ms | S3 PUT (2nd time) |
3 | 125ms | S3 PUT (3rd time) |
4 | 42ms | S3 PUT (4th time) |
5 | 44ms | S3 PUT (5th time) |
Congratulations, the second and subsequent times have become faster!
Since it became faster in verification 5, will it be faster from the first time if combined with Provisioned Concurrency? !!
The source is the same as Verification 5.
Number of times | Latency | Processing content |
---|---|---|
1 | 80ms | Initialization process |
2 | 370ms | S3 PUT (2nd time) * Because it has been executed once with the static initializer at the time of Provisioned |
3 | 43ms | S3 PUT (3rd time) |
4 | 72ms | S3 PUT (4th time) |
5 | 84ms | S3 PUT (5th time) |
** I did it! ** ** This is what I expected.
The final form is like this.
package helloworld;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
public class TestTarget0429 implements RequestHandler<Object, Object> {
private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
static{
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("dummy.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
}
public Object handleRequest(final Object input, final Context context) {
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));
System.out.println(result);
return null;
}
}
Number of times | Latency | Processing content |
---|---|---|
1 | 552ms | S3 PUT (2nd time) * Because it has been executed once with the static initializer at the time of Provisioned |
2 | 118ms | S3 PUT (3rd time) |
3 | 44ms | S3 PUT (4th time) |
4 | 86ms | S3 PUT (5th time) |
5 | 146ms | S3 PUT (6th time) |
Congratulations, congratulations.
Apparently, the Java class loader loads the class when the class is called for the first time, and it seems that the initial loading of the class takes time. So, it seems that if you load it once and load the class, it will be faster from the next time.
It would be nice if all the classes were loaded first, not when they were called, but isn't that possible?
The reference site is here.
Class method's blog https://dev.classmethod.jp/articles/report-best-practive-for-java-on-lambda/
Session materials at re: Invent 2019 https://d1.awsstatic.com/events/reinvent/2019/REPEAT_1_Best_practices_for_AWS_Lambda_and_Java_SVS403-R1.pdf https://youtu.be/ddg1u5HLwg8
Other blogs I found https://pattern-match.com/blog/2020/03/14/springboot2-and-aws-lambda-provisioned-concurrency/
Recommended Posts