【アップデート】20%のコスト削減と19%のパフォーマンスアップ!!LambdaのランタイムでGraviton2 processorが利用可能になりました

パフォーマンス向上とコスト削減が期待できる熱いアップデートです
2021.09.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

CX事業本部@大阪の岩田です。本日のアップデートにより、LambdaのランタイムでGraviton2 processorが利用可能になりました。

Lambdaの実行基盤で利用されているFirecrackerのロードマップを確認すると、2020年末ごろからArm CPUサポートがShipped状態になっていたので、LambdaのGraviton2対応がそろそろ来そうかな?と予想していたのですが、満を持してGraviton2対応がGAされました。

https://github.com/firecracker-microvm/firecracker/projects/13

何がうれしいのか

Graviton ProcessorとはAWSがカスタムビルドしたARM ベースのプロセッサーで、ハイパフォーマンスかつ低コストで利用できるのが最大のメリットです。前述のAWSのアナウンスによると、Graviton2 processorベースのLambdaは従来のx86よりも20%低いコストで19%優れたパフォーマンスを発揮するように設計されているとのことです。

今回更新されたLambdaの料金体系から東京リージョンにおけるLambdaの実行時間あたりの料金を比較すると、Graviton2 processorは従来のx86と比較して以下の通り約20%のコストダウンとなります。

アーキテクチャ 料金
x86 GB-秒あたり$0.0000166667
Arm(Graviton2 processor) GB-秒あたり$0.0000133334

AWS Lambda Pricing

さらに、単純な実行時間あたりの課金削減に加えて、パフォーマンス向上による実行時間の削減にも期待できます。例えば、これまでx86で100ミリ秒かかっていた処理がGraviton2 processorで80ミリ秒で終わると仮定すると、20ミリ秒分の課金が削減できることになります。

すでにAWSのブログが公開されていますが、こちらのブログの検証パターンではGraviton2 processorにより

  • 約30%の高速化
  • 約44%のコスト削減

が実現できるようです

すでにEC2やRDSなどのサービスではGraviton Processor/Graviton2 processorを利用するインスタンスタイプが利用可能になっていましたが、今回のアップデートによりLambdaのランタイムでもGraviton2 processorが利用可能になりました。

Graviton2 processorは全てのランタイムで利用できるわけではなく、Amazon Linux2ベースのランタイムでのに利用できます。つまりPython3.7のようなAmazon LinuxベースのランタイムではGraviton2 processorは利用できません。また、すでにEnd of support phase 1を迎えているNode.js10でも同様にGraviton2 processorは利用できません。

Lambda ランタイム

具体的には現時点で

  • Node.js 12 、 14
  • Python 3.8 、 3.9
  • Java 8 (java8.al2) 、 11
  • .NET Core 3.1
  • Ruby 2.7
  • Custom Runtime(provided.al2)

がGraviton2 processorをサポートしています

試しにPython3.7のLambdaのアーキテクチャをarm64に更新してみたところ

Runtime python3.7 does not support the following architectures [arm64]. Please select different architectures from [x86_64, arm64] or select a different runtime.

というエラーが発生し、更新に失敗しました。

やってみる

実際にGraviton2 processorを利用するLambdaの動作を確認してみます

/proc/cpuinfoの確認

試しにLambdaのランタイムにNode.js 14.xを指定し、以下のコードを実行。

const execSync = require('child_process').execSync;

exports.handler = async (event) => {
    
    const cmd = 'cat /proc/cpuinfo';
    const result =  execSync(cmd).toString();
    console.log("\n"+result); 
    
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

アーキテクチャに

  • x86_64
  • arm64

それぞれを指定した場合の出力を比較してみました。なお、Lambdaのメモリ割り当てはデフォルトの128Mとしています。

x86_64の場合の出力はこちら

processor	: 0
vendor_id	: GenuineIntel
cpu family	: 6
model		: 63
model name	: Intel(R) Xeon(R) Processor @ 2.50GHz
stepping	: 2
microcode	: 0x1
cpu MHz		: 2500.008
cache size	: 36608 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 2
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm cpuid_fault invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid smap xsaveopt arat md_clear arch_capabilities
bugs		: spectre_v1 spectre_v2 spec_store_bypass swapgs
bogomips	: 5000.01
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:

processor	: 1
vendor_id	: GenuineIntel
cpu family	: 6
model		: 63
model name	: Intel(R) Xeon(R) Processor @ 2.50GHz
stepping	: 2
microcode	: 0x1
cpu MHz		: 2500.008
cache size	: 36608 KB
physical id	: 0
siblings	: 2
core id		: 1
cpu cores	: 2
apicid		: 1
initial apicid	: 1
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm cpuid_fault invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid smap xsaveopt arat md_clear arch_capabilities
bugs		: spectre_v1 spectre_v2 spec_store_bypass swapgs
bogomips	: 5000.01
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:

arm64(Graviton2 processorの場合)の出力はこちら

processor	: 0
BogoMIPS	: 243.75
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp ssbs
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x3
CPU part	: 0xd0c
CPU revision	: 1

processor	: 1
BogoMIPS	: 243.75
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp ssbs
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x3
CPU part	: 0xd0c
CPU revision	: 1

UnixBenchの比較

せっかくなので従来のx86環境とGraviton2環境を比較してみます。CPUのベンチマークといえばUnixBenchが定番ですね。Lambdaのパッケージ形式としてコンテナイメージを選択した場合もGraviton2 processorが利用できるので、UnixBenchが利用できるコンテナイメージを作成してLambda実行環境で動かしてみます。

まずDockerfileです

# x86向けのイメージのベース
FROM amazon/aws-lambda-provided:al2.2021.09.13.11

# Graviton2向け向けのイメージのベース
# FROM amazon/aws-lambda-provided:al2
RUN yum install -y git gcc make && \
    git clone https://github.com/kdlucas/byte-unixbench.git && \
    cd byte-unixbench/UnixBench/ && \
    make
COPY bootstrap /var/runtime/
COPY bootstrap function.sh /var/task/
RUN chmod 755 /var/runtime/bootstrap /var/task/function.sh
CMD ["function.handler"]
ENV UB_RESULTDIR /tmp
ENV UB_TMPDIR /tmp

デプロイするLambdaの設定がx86かarm64(Graviton2 processor)かに応じてFROMで指定するベースのイメージを切り替えて下さい

Dockerfile内でCOPYしているbootstrapです

#!/bin/sh

set -euo pipefail

# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

# Processing
while true
do
  HEADERS="$(mktemp)"
  # Get an event. The HTTP request will block until one is received
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")

  # Extract request ID by scraping response headers received above
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # Run the handler function from the script
  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

  # Send the response
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
done

続いてfunction.sh

#!/bin/bash
function handler() {
  cd /var/task/byte-unixbench/UnixBench/  
  ./Run -i 5 dhrystone whetstone execl pipe spawn
}

こちらがLambdaのハンドラーから呼び出されることになります。テスト実行対象として

  • dhrystone
  • whetstone
  • execl
  • pipe
  • spawn

を指定しつつ、Lambdaの実行時間上限である15分以内に処理が完了するようにテスト回数を5に減らしています

あとはx86環境用とarm環境用それぞれのコンテナイメージをビルドしてECRにプッシュします

docker build -t cm-iwata/lambda-unixbench:x86_64 .
docker tag cm-iwata/lambda-unixbench:x86_64 <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-unixbench:x86_64
aws ecr get-login-password  --region ap-northeast-1 | docker login --username AWS --password-stdin <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
docker push  <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-unixbench:x86_64

プッシュできたら作成したコンテナイメージを参照するようにLambda関数を作成し、以下の設定を編集します

  • メモリ(MB)...1792MB
    • Lambdaはメモリ割当1.8Gから1vCpu分のフルパワーが利用できるようになります
  • タイムアウト...15分

設定できたらテスト実行してUnixBenchの出力を確認しましょう。結果は以下の通りでした

結果(x86_64)

make all
make[1]: Entering directory `/var/task/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory `/var/task/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory `/var/task/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory `/var/task/byte-unixbench/UnixBench'
make[2]: Nothing to be done for `programs'.
make[2]: Leaving directory `/var/task/byte-unixbench/UnixBench'
make[1]: Leaving directory `/var/task/byte-unixbench/UnixBench'

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com

------------------------------------------------------------------------------
   Use directories for:
      * File I/O tests (named fs***) = /tmp
      * Results                      = /tmp
------------------------------------------------------------------------------


1 x Dhrystone 2 using register variables  1 2 3 4 5

1 x Double-Precision Whetstone  1 2 3 4 5

1 x Execl Throughput  1 2

1 x Pipe Throughput  1 2 3 4 5

1 x Process Creation  1 2

2 x Dhrystone 2 using register variables  1 2 3 4 5

2 x Double-Precision Whetstone  1 2 3 4 5

2 x Execl Throughput  1 2

2 x Pipe Throughput  1 2 3 4 5

2 x Process Creation  1 2

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: : GNU/Linux
   OS: GNU/Linux -- 4.14.243-194.434.amzn2.x86_64 -- #1 SMP Tue Aug 17 23:23:16 UTC 2021
   Machine: x86_64 (x86_64)
   Language: en_US.utf8 (charmap=\"UTF-8\", collate=\"UTF-8\")
   CPU 0: Intel(R) Xeon(R) Processor @ 2.50GHz (5000.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   CPU 1: Intel(R) Xeon(R) Processor @ 2.50GHz (5000.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   ; runlevel 

------------------------------------------------------------------------
Benchmark Run: Thu Sep 30 2021 07:41:48 - 07:47:28
2 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       37534408.4 lps   (10.0 s, 4 samples)
Double-Precision Whetstone                     4859.5 MWIPS (9.9 s, 4 samples)
Execl Throughput                               3928.1 lps   (29.9 s, 2 samples)
Pipe Throughput                             1517401.9 lps   (10.0 s, 4 samples)
Process Creation                               7633.7 lps   (30.0 s, 2 samples)

System Benchmarks Partial Index              BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   37534408.4   3216.3
Double-Precision Whetstone                       55.0       4859.5    883.5
Execl Throughput                                 43.0       3928.1    913.5
Pipe Throughput                               12440.0    1517401.9   1219.8
Process Creation                                126.0       7633.7    605.8
                                                                   ========
System Benchmarks Index Score (Partial Only)                         1139.2

------------------------------------------------------------------------
Benchmark Run: Thu Sep 30 2021 07:47:28 - 07:53:05
2 CPUs in system; running 2 parallel copies of tests

Dhrystone 2 using register variables       37851790.7 lps   (10.0 s, 4 samples)
Double-Precision Whetstone                     5320.4 MWIPS (9.2 s, 4 samples)
Execl Throughput                               3462.3 lps   (29.5 s, 2 samples)
Pipe Throughput                             1537607.3 lps   (10.0 s, 4 samples)
Process Creation                               8282.3 lps   (30.0 s, 2 samples)

System Benchmarks Partial Index              BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   37851790.7   3243.5
Double-Precision Whetstone                       55.0       5320.4    967.3
Execl Throughput                                 43.0       3462.3    805.2
Pipe Throughput                               12440.0    1537607.3   1236.0
Process Creation                                126.0       8282.3    657.3
                                                                   ========
System Benchmarks Index Score (Partial Only)                         1154.7

結果(arm64)

make all
make[1]: Entering directory `/var/task/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory `/var/task/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory `/var/task/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory `/var/task/byte-unixbench/UnixBench'
make[2]: Nothing to be done for `programs'.
make[2]: Leaving directory `/var/task/byte-unixbench/UnixBench'
make[1]: Leaving directory `/var/task/byte-unixbench/UnixBench'

#    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
#    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
#    #  # #  #  #    ##            #####   #####   # #  #  #       ######
#    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
#    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
####   #    #  #  #    #          #####   ######  #    #   ####   #    #

Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

Multi-CPU version                  Version 5 revisions by Ian Smith,
Sunnyvale, CA, USA
January 13, 2011                   johantheghost at yahoo period com

------------------------------------------------------------------------------
Use directories for:
* File I/O tests (named fs***) = /tmp
      * Results                      = /tmp
------------------------------------------------------------------------------


1 x Dhrystone 2 using register variables  1 2 3 4 5

1 x Double-Precision Whetstone  1 2 3 4 5

1 x Execl Throughput  1 2

1 x Pipe Throughput  1 2 3 4 5

1 x Process Creation  1 2

2 x Dhrystone 2 using register variables  1 2 3 4 5

2 x Double-Precision Whetstone  1 2 3 4 5

2 x Execl Throughput  1 2

2 x Pipe Throughput  1 2 3 4 5

2 x Process Creation  1 2

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: : GNU/Linux
   OS: GNU/Linux -- 4.14.243-194.434.amzn2.aarch64 -- #1 SMP Tue Aug 17 23:22:56 UTC 2021
   Machine: aarch64 (aarch64)
   Language: en_US.utf8 (charmap=\"UTF-8\", collate=\"UTF-8\")
   ; runlevel 

------------------------------------------------------------------------
Benchmark Run: Thu Sep 30 2021 06:40:31 - 06:46:08
2 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       41879150.0 lps   (10.0 s, 4 samples)
Double-Precision Whetstone                     5925.2 MWIPS (9.7 s, 4 samples)
Execl Throughput                               5401.7 lps   (30.0 s, 2 samples)
Pipe Throughput                             1721879.1 lps   (10.0 s, 4 samples)
Process Creation                               9100.0 lps   (30.0 s, 2 samples)

System Benchmarks Partial Index              BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   41879150.0   3588.6
Double-Precision Whetstone                       55.0       5925.2   1077.3
Execl Throughput                                 43.0       5401.7   1256.2
Pipe Throughput                               12440.0    1721879.1   1384.1
Process Creation                                126.0       9100.0    722.2
                                                                   ========
System Benchmarks Index Score (Partial Only)                         1371.6

------------------------------------------------------------------------
Benchmark Run: Thu Sep 30 2021 06:46:08 - 06:51:51
2 CPUs in system; running 2 parallel copies of tests

Dhrystone 2 using register variables       42419526.5 lps   (10.0 s, 4 samples)
Double-Precision Whetstone                     6027.4 MWIPS (9.6 s, 4 samples)
Execl Throughput                               4147.8 lps   (29.6 s, 2 samples)
Pipe Throughput                             1716980.9 lps   (10.0 s, 4 samples)
Process Creation                               8764.6 lps   (30.0 s, 2 samples)

System Benchmarks Partial Index              BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   42419526.5   3634.9
Double-Precision Whetstone                       55.0       6027.4   1095.9
Execl Throughput                                 43.0       4147.8    964.6
Pipe Throughput                               12440.0    1716980.9   1380.2
Process Creation                                126.0       8764.6    695.6
                                                                   ========
System Benchmarks Index Score (Partial Only)                         1298.3

今回はメモリの割当を1792MBで実行しているので、1並列でテストを実行した結果で比較するのが良さそうですね。スコアとしては

  • x86...1139.2
  • arm64(Graviton2)...1371.6

という結果でした。Lambda実行環境のデプロイ先としてどのハードウェアを引き当てるかというガチャ要素があるため、本来はもう少しテスト回数を重ねたいところではありますが、概ねカタログスペックである19%近くスコアがアップしていることが確認できました。

まとめ

Lambdaをバリバリ活用されているシステムでは

  • 処理の高速化
  • コスト削減

ともに期待できる非常にうれしいアップデートではないでしょうか?

既存のコードがそのまま正しく動作するかの検証は必要ですが、もしGraviton2 processorに移行できれば大きなメリットが享受できそうです。既存のワークロードをGraviton2 processorに移行できないか、是非一度検証してみて下さい。

参考