# AWS Glue の Spark / Ray / Python Shell 比較

## はじめに
2023年6月5日のGAからAWS GlueでRayが使えるようになりました(参考: [AWS Glue for Ray が一般利用可能に](https://aws.amazon.com/jp/about-aws/whats-new/2023/06/aws-glue-ray-generally-available/))。 これはSparkやPython Shellに続く、3つめのエンジンとして利用できるようになったオプションです。

本記事では、Glueで活用できる3つのエンジンを使った感想や、機能差などについてご紹介するものです。大規模データを活用した徹底検証というよりはむしろ、簡単な検証を通じて3エンジンの使用感や大まかな特徴をおさえることを主眼にしています。

## 目的
簡単な処理を通じて以下を体験的に理解する。
- 使用可能な機能
- 使用感

## 記事の想定読者
- Glue初心者
- 3エンジンの違いを知りたい人向け

## 試したこと
- S3に格納したCSVファイルを読み込み、レコード件数をカウントするだけという簡単な集計を行った
- データの規模は小、中、大とあり、それぞれ以下の通り(合計は小数点第2位四捨五入)
  - 小：4.3 MB × 24 ファイル (合計：103.2 MB)
  - 中：43 MB × 24 ファイル (合計：1.0 GB)
  - 大：43 MB × 96 ファイル (合計：4.1 GB)
- ワーカータイプやワーカー数、エンジンを変えて比較してみる

## 結果・コメント
実際に試したケースと、その結果は以下の通りだった。

|  No. | エンジン     | データ量   | ワーカータイプ   | ワーカー数   | 初期化時間   | 読込時間   | 件数カウント時間   |
|------:|:-------------|:-----------|:-----------------|:-------------|:-------------|:-----------|:-------------------|
|     1 | Python Shell | 小         | ー               | ー           | ー           | 5.08 s     | < 0.01 s            |
|     2 | Python Shell | 中         | ー               | ー           | ー           | 33.57 s    | < 0.01 s            |
|     3 | Python Shell | 大         | ー               | ー           | ー           | 132.56 s   | < 0.01 s            |
|     4 | Spark        | 小         | G 1X             | 2            | 4.70 s       | 13.59 s    | 2.82 s             |
|     5 | Spark        | 小         | G 2X             | 4            | 3.44 s       | 10.18 s    | 2.06 s             |
|     6 | Spark        | 中         | G 1X             | 2            | 3.74 s       | 21.84 s    | 6.32 s             |
|     7 | Spark        | 中         | G 2X             | 4            | 3.53 s       | 10.09 s    | 4.77 s             |
|     8 | Spark        | 中         | G 4X             | 4            | 3.06 s       | 7.13 s     | 3.20 s             |
|     9 | Spark        | 大         | G 1X             | 2            | 4.89 s       | 24.12 s    | 19.33 s            |
|    10 | Spark        | 大         | G 1X             | 4            | 4.61 s       | 24.07 s    | 8.26 s             |
|    11 | Spark        | 大         | G 1X             | 7            | 4.24 s       | 21.65 s    | 7.90 s             |
|    12 | Spark        | 大         | G 2X             | 4            | 3.48 s       | 18.21 s    | 6.04 s             |
|    13 | Spark        | 大         | G 4X             | 7            | 2.29 s       | 14.07 s    | 2.91 s             |
|    14 | Ray          | 小         | Z 2X             | 1            | 0.028 s      | 2.38 s     | 3.23 s             |
|    15 | Ray          | 小         | Z 2X             | 3            | 0.028 s      | 2.35 s     | 3.21 s             |
|    16 | Ray          | 中         | Z 2X             | 1            | 0.028 s      | 2.32 s     | 9.00 s             |
|    17 | Ray          | 中         | Z 2X             | 3            | 0.030 s      | 2.37 s     | 9.64 s             |
|    18 | Ray          | 大         | Z 2X             | 1            | 0.029 s      | 2.39 s     | 17.89 s            |
|    19 | Ray          | 大         | Z 2X             | 3            | 0.028 s      | 2.38 s     | 15.32 s            |
|    20 | Ray          | 大         | Z 2X             | 6            | 0.029 s      | 2.35 s     | 13.09 s            |
|    21 | Ray          | 大         | Z 2X             | 12           | 0.029 s      | 2.42 s     | 13.49 s            |

またそれぞれのエンジンで使用できる機能について、どれだけ選択の幅があるかも調べた。  
その結果は以下の通り。  

| 機能                | Spark                                          | Ray                             | Python Shell                              |
|--------------------|-----------------------------------------------|--------------------------------|------------------------------------------|
| ワーカータイプ      | ○ (G 1X, G 2X, G 4X, G 8Xから選択可)           | × (Z 2X (8vCPU 64 GB RAM) 固定) | △ (1 DPU, 1/16 DPUから選択可)             |
| ワーカー数          | ○ (最小値は2)                                  | ○ (最小値は1)                   | ×                                         |
| バージョン設定など  | ○ (Glue versionは2.0, 3.0, 4.0, 5.0から選択可) | × (Glue 4.0固定)                | ○ (Python バージョンは3.9, 3.6から選択可) |
| job timeout         | ○ (1分~10,080分(=7日))                         | × (480分 固定)                  | ○  (正定値であればよい)                    |
| Flex execution      | ○ (使用可)                                     | × (使用不可)                    | × (使用不可)                              |
| Spark UI            | ○ (使用可)                                     | × (使用不可)                    | × (使用不可)                              |
| Maximum concurrency | ○ (使用可)                                     | × (使用不可)                    | × (使用不可)                              |
| Job bookmark        | ○ (使用可)                                     | × (使用不可)                    | × (使用不可)                              |
| Job Run Queueing    | ○ (使用可)                                     | ○ (使用可)                      | ○ (使用可)                                |
| Notebook            | ○ (使用可)                                     | × (使用不可)                    | × (使用不可)                              |
| Visual ETL          | ○ (使用可)                                     | × (Rayは使用不可)               | × (使用不可)                              |


### Python Shell
- データ量が多くなるにつれファイル読込に時間が掛かっている
- **シングルノードで一か所にデータが集約されるからか、最後の集計が1秒かからずに終了した**
    - 実測ではマイクロ秒単位で集計が終了した

### Spark
- Python Shellよりも**ファイル読込の時間が短縮**されている
- 最初に数秒程度、初期化(Spark Session立ち上げ)に時間を要するが、**ワーカー数やワーカータイプを変更しても特筆すべき変化は無かった**
- データ規模大のとき、ワーカータイプやワーカー数の違いで件数カウント時間他より大きく差が開いていた
- "Sparkは難しい"という先入観があったが、**比較的Pandasに近いスクリプトで実現できた**
  - PySparkのおかげ
  - それにSparkセッションの初期化に関する部分は最初から書かれていて(下図参照)、具体的な処理を書くところに専念できた
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3857096/186a9bed-70a0-4159-a565-bde5e03bf65c.png)

### Ray
- **Pythonのコードをそのまま使える使用感**
- そのうえで並列処理の恩恵を受けられて処理が高速化されていた(特に**ファイル読込時間**)
- プレビュー時と比較すると、いくらか変更点がある
    1. **modin, dask**などが提供されるモジュールに含まれなくなった
        - modinはRay版Koalas(Pandas on Apache Spark)のようなものでPandasと同じ書き方でコーディングしながら、かつエンジンにRayを使って純粋なPandasよりも高速化できる
        - modinを使うにはユーザーが設定するしかない
    2. プレビュー時はNotebookのカーネルにRayを指定出来たが現在それは出来なくなった
- ログファイルとしてS3に以下が作成される
  ```
  job_name
    ├─cluster_logs
    │       coordinator-8acb863a-6342-19c1-2229-a8c098bcea92-cluster_logs.zip
    │       worker-4ccb863a-888b-a92c-f1e3-9e590555a764-cluster_logs.zip
    │       worker-7ccb863a-8888-3fb1-34ec-901834f9d5b5-cluster_logs.zip
    │
    └─job-result
            metadata
            result
            stderr
            stdout
  ```
  - `print()`などの出力は上の**stdout**に出力されている

## おわりに
AWS Glueでは、**柔軟な設定や機能の豊富さを求める場合にはSparkが優れている**一方で、**すでにPythonでジョブを作成している場合にはRay**を使うことで効率化が期待できます。**軽量な処理であれば、Python Shell**を利用する方がコスト面で有利です。  

最小構成でも複数のコアによって並列処理が行われており、基本的な分散処理は成立しています。**ワーカー数やタイプによる性能向上は必ずしも線形ではなく**、効果には限界があります。

また今回の検証ケースにおいては、**初期化時間はRayの方が短く、CSVの読み込みも高速**でしたが、**集計処理はSpark**の方が優れていました。処理内容に応じて使い分けることが重要だと感じました。