adam bien's blog

AWS Lambda: The Impact of Reflection and Annotations on AWS Lambda's Performance 📎

What is the performance difference between an AWS Lambda calling a method directly and an AWS Lambda using reflection for searching a method annotated with the annotation?

Both Lambdas were deployed with the smallest possible amount of RAM (128 MB) and therefore, a fraction of the CPU.

The NoReflection Lambda invoked the method greetings directly:


public class NoReflection {

    public String onEvent(Map<String, String> event) {
        return this.greetings();
    }

    public String greetings(){
        return "hello, no reflection " + System.currentTimeMillis();
    }
    
}    

The Reflection AWS Lambda searched for the method greetings annotated with:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface InvokeMe {}

...and then invoked the found method via reflection:

import java.lang.reflect.Method;
import java.util.Map;

public class Reflection {    

    public String onEvent(Map<String, String> event) {
        var methods = Reflection.class.getMethods();
        
        for (Method method : methods) {
            if(method.getAnnotation(InvokeMe.class) != null){
                try {
                    return (String) method.invoke(this);
                } catch (Exception e) {
                    throw new IllegalStateException("Cannot invoke method",e);
                }
            }
        }
        return "no annotated method found";
    }

    @InvokeMe
    public String greetings(){
        return "hello, with reflection " + System.currentTimeMillis();
    }
}       

Spoiler: the direct invocation was faster:

AWS CDK v2 and "Simplest Possible AWS Lambda Function with Cloud Development Kit (CDK) Boilerplate" was used for deployment:

import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.lambda.Code;
import software.amazon.awscdk.services.lambda.Function;
import software.amazon.awscdk.services.lambda.Runtime;
import software.constructs.Construct;

public class LambdaStack extends Stack {

    static int memory = 128;
    
    public LambdaStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);
        
        var noReflectionName  = "airhacks_lambda_reflection_performance_boundary_NoReflection";
        var noReflectionHandler = "airhacks.lambda.reflection.boundary.NoReflection::onEvent";

        createFunction(noReflectionName, noReflectionHandler, memory);


        var reflectionName  = "airhacks_lambda_reflection_performance_boundary_Reflection";
        var reflectionHandler = "airhacks.lambda.reflection.boundary.Reflection::onEvent";

        createFunction(reflectionName, reflectionHandler, memory);
        
    }
    
    Function createFunction(String functionName,String functionHandler, int memory) {
        return Function.Builder.create(this, functionName)
                .runtime(Runtime.JAVA_11)
                .code(Code.fromAsset("../target/function.jar"))
                .handler(functionHandler)
                .memorySize(memory)
                .functionName(functionName)
                .build();
    }
    
}