言語別AWS SDKのパフォーマンスの一検証

# 背景
## タグについて
AWSはリソースに[タグ](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/Using_Tags.html)の付与が可能です。AWSにおけるタグはリソースの管理や[コスト配分](http://blog.serverworks.co.jp/tech/2019/07/29/cost-alloc-tags-basic/)の用途で用いられ、タグの付与設計はAWSの運用において非常に重要なポイントとなります。
特に大規模な環境や組織になるとリソースに付与するタグは非常に多くなる傾向があります。

## AWS SDK for Rubyのパフォーマンス
[こちらの記事](https://qiita.com/kure/items/19b1c17a56d81e2bb955)でも言及したようにAWS SDK for Ruby では、リソースにタグが大量に付与されている場合、一度に大量のリソースを取得に大幅に時間がかかることがあります。
以下はEBSを例に、タグ付与のあり・なしとで、リソースの取得（describe_volumes）の完了にどれくらいの時間を要したかを比較したグラフです（以下画像は前述記事の再掲）。
タグが大量に（この例では20個）付与されている場合、リソースの取得完了までに非常に時間がかかることが分かります。

![ruby_describe.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50690/c0c6ebe0-8bd6-3c83-0e08-05e0ef865788.png)


[こちらの記事](https://qiita.com/kure/items/19b1c17a56d81e2bb955)では、処理に時間がかかっていたのはAPIのレスポンスのXMLをRubyのオブジェクトにパースする部分であると推定しました。

## 疑問
上記のようなパフォーマンスの変化が、AWS SDKの他の言語でも発生するか気になったので、確認してみようと思いました。

# 検証
今回、Rubyに加えて、Python、PHP、Go、Node.jsのAWS SDKでパフォーマンスを測定してみました。
対象は **タグが20個付与されたEBS1000個** です。
クライアントは東京リージョンのLightSail(Ubuntu 18.04)です。
各言語で3回実施し、平均値を取っています。
各言語とSDKのVersionは後述。

## 検証結果

検証結果は以下の通りでした。どうやら **非常に時間がかかるのはRubyのSDKのみ** のようでした。
他の言語については、本件に関する懸念はとりあえずは無くて良さそうです。

![秒数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50690/75575f26-140e-67b9-010e-d21bb348ccb2.png)

# おわりに/感想
今回のような構成で処理に非常に時間がかかるのはAWS SDK for Ruby のみのようです。
RubyのSDKについても内部的に使用されるライブラリの改善等でパフォーマンスは改善されるかもしれませんので、今後に期待したいところです。

# 各言語とSDKのVersionと検証コード

|         | 言語    | SDK      |
|---------|---------|----------|
| Ruby    | 2.6.3   | 3.89.1   |
| PHP     | 7.2     | 3.133.34 |
| Python  | 3.7.1   | 1.2      |
| Node.js | 12.14.0 | 2.638.0  |
| Go      | 1.13.4  | 1.29.22  |

## Ruby

```ruby
require 'aws-sdk-ec2'

Aws.config.update(credentials: Aws::Credentials.new('',''))
ec2_client = Aws::EC2::Client.new(region: 'ap-northeast-1')

start_time = Time.now

ec2_client.describe_volumes

end_time = Time.now
colapsed_time = end_time - start_time
puts colapsed_time
```

## PHP

```php
<?php
require 'vendor/autoload.php';

use Aws\Ec2\Ec2Client;

$ec2Client = new Aws\Ec2\Ec2Client([
  'region' => 'ap-northeast-1',
  'version' => '2016-11-15',
]);

$time_start = microtime(true);

$result = $ec2Client->describeVolumes();

$time = microtime(true) - $time_start;
echo "{$time} 秒";
```

## Python

```python
import boto3
import time

ec2 = boto3.client('ec2')
start = time.time()

ec2.describe_volumes()

elapsed_time = time.time() - start
print ("elapsed_time:{0}".format(elapsed_time))
```

## Node.js

```javascript
var AWS = require('aws-sdk');

AWS.config.update({region: 'ap-northeast-1'});

var ec2 = new AWS.EC2();
const startTime = Date.now(); // 開始時間
ec2.describeVolumes({}, function (err, data) {
  console.log(Date.now() - startTime);
});
```

## Go

```go
package main

import (
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

func main() {
    sess, _ := session.NewSession(&aws.Config{
        Region: aws.String("ap-northeast-1")},
    )

    svc := ec2.New(sess)
    input := &ec2.DescribeVolumesInput{}

    start := time.Now()
    svc.DescribeVolumes(input)
    end := time.Now()
    fmt.Printf("%f秒\n", (end.Sub(start)).Seconds())
}
```