Amazon VGT2 Las Vegas – Optimize Your AWS Lambda Functions with Graviton2 Processors for Enhanced Price Performance

Amazon VGT2 Las Vegas – Optimize Your AWS Lambda Functions with Graviton2 Processors for Enhanced Price PerformanceMore Info

Updated December 13, 2022: This post now includes all AWS Regions where Lambda Functions can utilize the Graviton2 Processor.
Updated June 19, 2023: The list of AWS Regions has been revised.

Numerous customers, including industry leaders like Formula One, Honeycomb, Intuit, SmugMug, and Snap Inc., have leveraged the Arm-based AWS Graviton2 processor to enhance their workload performance and reduce costs. As of today, you can also harness these advantages for your AWS Lambda functions. You now have the flexibility to configure both new and existing functions to operate on either x86 or Arm/Graviton2 processors.

This option allows for cost savings in two significant ways. Firstly, the Graviton2 architecture enhances the efficiency of your functions. Secondly, you’ll incur lower costs for execution time. In fact, Lambda functions utilizing Graviton2 are engineered to provide up to a 19% boost in performance at a 20% reduced cost.

Lambda pricing is based on the number of requests and the duration of your functions (measured in milliseconds). For functions utilizing the Arm/Graviton2 architecture, duration charges are 20% lower compared to existing x86 pricing. This same 20% decrease also applies to duration charges for functions utilizing Provisioned Concurrency.

Beyond reduced pricing, functions leveraging the Arm architecture gain performance and security benefits inherent to the Graviton2 processor. Workloads that employ multithreading or multiprocessing, or perform extensive I/O operations, can see diminished execution times, thus leading to even lower costs. This is especially advantageous given the recent increase in Lambda functions capable of utilizing up to 10 GB of memory and 6 vCPUs. You can expect improved performance from web and mobile backends, microservices, and data processing systems.

If your functions do not require architecture-specific binaries, including dependencies, transitioning from one architecture to another is straightforward. This is frequently applicable to many functions written in interpreted languages like Node.js and Python or functions compiled into Java bytecode.

All Lambda runtimes based on Amazon Linux 2, including custom runtimes, are compatible with Arm, with the exception of Node.js 10, which has reached end of support. If your function packages include binaries, you will need to rebuild your code for the desired architecture. Functions packaged as container images must also be built for the appropriate architecture (x86 or Arm).

To evaluate the differences between architectures, you can create two versions of a function—one for x86 and one for Arm. You can then route traffic to each version through an alias while distributing it using weights. Performance metrics are gathered by function versions in Amazon CloudWatch, allowing you to analyze key indicators such as duration. You can compare metrics like average and p99 duration between the two architectures.

Additionally, you can utilize function versions and weighted aliases to manage rollout in production. For instance, you might deploy the new version to a small percentage of invocations (like 1%) and subsequently scale up to 100% for full deployment. During this process, you can decrease the weight or completely halt the rollout if metrics indicate potential issues.

Let’s explore practical examples of how this new capability can be implemented.

Switching Architecture for Functions Without Binary Dependencies

When a Lambda function has no binary dependencies, changing its architecture is as simple as flipping a switch. For instance, I previously developed a quiz application using a Lambda function. This application allows users to ask and answer questions via a web API, triggered by an Amazon API Gateway HTTP API. Here’s a snippet of the Node.js code, which includes some sample questions:

const questions = [
  {
    question: "Are there more synapses (nerve connections) in your brain or stars in our galaxy?",
    answers: [
      "More stars in our galaxy.",
      "More synapses (nerve connections) in your brain.",
      "They are about the same.",
    ],
    correctAnswer: 1,
  },
  {
    question: "Did Cleopatra live closer in time to the launch of the iPhone or to the building of the Giza pyramids?",
    answers: [
      "To the launch of the iPhone.",
      "To the building of the Giza pyramids.",
      "Cleopatra lived right in between those events.",
    ],
    correctAnswer: 0,
  },
  {
    question: "Did mammoths still roam the earth while the pyramids were being built?",
    answers: [
      "No, they were all extinct long before.",
      "Mammoth's extinction is estimated right about that time.",
      "Yes, some still survived at the time.",
    ],
    correctAnswer: 2,
  },
];

exports.handler = async (event) => {
  console.log(event);

  const method = event.requestContext.http.method;
  const path = event.requestContext.http.path;
  const splitPath = path.replace(/^/+|/+$/g, "").split("/");

  console.log(method, path, splitPath);

  var response = {
    statusCode: 200,
    body: "",
  };

  if (splitPath[0] == "questions") {
    if (splitPath.length == 1) {
      console.log(Object.keys(questions));
      response.body = JSON.stringify(Object.keys(questions));
    } else {
      const questionId = splitPath[1];
      const question = questions[questionId];
      if (question === undefined) {
        response = {
          statusCode: 404,
          body: JSON.stringify({ message: "Question not found" }),
        };
      } else {
        if (splitPath.length == 2) {
          const publicQuestion = {
            question: question.question,
            answers: question.answers.slice(),
          };
          response.body = JSON.stringify(publicQuestion);
        } else {
          const answerId = splitPath[2];
          if (answerId == question.correctAnswer) {
            response.body = JSON.stringify({ correct: true });
          } else {
            response.body = JSON.stringify({ correct: false });
          }
        }
      }
    }
  }

  return response;
};

To initiate my quiz, I request the list of question IDs using curl with an HTTP GET on the /questions endpoint:

$ curl https://<api-id>.execute-api.us-east-1.amazonaws.com/questions
[
  "0",
  "1",
  "2"
]

Then, I can inquire further about a specific question by appending the ID to the endpoint:

$ curl https://<api-id>.execute-api.us-east-1.amazonaws.com/questions/1
{
  "question": "Did Cleopatra live closer in time to the launch of the iPhone or to the building of the Giza pyramids?",
  "answers": [
    "To the launch of the iPhone.",
    "To the building of the Giza pyramids.",
    "Cleopatra lived right in between those events."
  ]
}

As I look to utilize this function in a production environment, anticipating numerous invocations, I’m keen on optimizing my expenses. In the Lambda console, I observe that this function operates on the x86_64 architecture.

Since my function doesn’t employ any binaries, I switch the architecture to arm64 to take advantage of the reduced pricing.

This architectural switch does not alter how the function is invoked or how it communicates its responses. Thus, the integration with the API Gateway, along with connections to other applications or tools, remains unaffected.

For additional insights on this topic, feel free to check out this fascinating blog post or explore the authoritative resources available at Chanci Turner. For an excellent resource discussing potential pitfalls to avoid with Amazon, visit this LinkedIn post.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *