大規模データの処理の実行速度の比較

この文書は2022年12月17日に開催されたR研究集会2022での発表「Rによる大規模データの処理」の補足資料です。 発表の中で扱った{arrow}パッケージによる大規模データの処理の実行速度を複数のRパッケージと比較した結果を示します。

arrow導入編

実行環境と検証内容

環境

ここでの検証結果は次の環境で行いました。

  • Apple M1 Mac (メモリ: 64GB)
  • R version 4.2.1 (2022-06-23)

検証内容

データ読み込みと2つのデータ操作の実行速度を、{arrow}といくつかのパッケージ間で比較します。具体的な検証用のコードと実行速度のベンチマークの計測方法に関してはGitHub上のコードを参照してください。

  1. csvファイルの読み込み… arrow::read_csv_arrow(), readr::read_csv(), data.table::fread()
  2. グループ化と集計
  3. 結合

パッケージの読み込み

{arrow}パッケージの他に、処理速度の比較対象となるパッケージを読み込みます。

library(arrow) # 10.0.1
library(dplyr) # 1.0.10
library(data.table) # 1.14.6
library(readr) # 2.1.3
library(dtplyr) # 1.2.2
library(duckdb) # 0.6.1
library(ggplot2)
source(here::here("R/plot.R"))
source(here::here("R/schema.R")) # 断面交通量情報データのためのスキーマ定義

# ベンチマーク結果などの読み込み用
pins_resources_local <-
  pins::board_folder(here::here("data-raw"))

断面交通量情報データ

  • 公益財団法人日本道路交通情報センター https://www.jartic.or.jp が提供
  • 各都道府県警察が車両感知器などの計測機器で収集した断面交通量に関する情報を警察庁においてとりまとめたもの
    • 毎月、都道府県警察(北海道警察は5方面)ごとに.zipファイルが更新される
# 日本道路交通情報センター提供のデータを読み込むパッケージ
remotes::install_github("uribo/jarticr")

各ファイルの中身は次のようになっています。

dplyr::glimpse(
  jarticr::read_jartic_traffic(
    here::here("data-raw/typeB_tokushima_2022_10/徳島県警_202210.csv")))
Rows: 2,384,773
Columns: 10
$ datetime        <dttm> 2022-10-01, 2022-10-01, 2022-10-01, 2022-10-01, 2022-…
$ source_code     <chr> "3028", "3028", "3028", "3028", "3028", "3028", "3028"…
$ location_no     <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,…
$ location_name   <chr> "徳島本町→県庁前", "県庁前→徳島本町", "新助任橋北詰→徳…
$ meshcode10km    <chr> "513404", "513404", "513404", "513404", "513404", "513…
$ link_type       <int> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, …
$ link_no         <int> 710, 705, 681, 676, 679, 678, 11, 375, 6, 5, 6, 5, 842…
$ traffic         <int> 21, 24, 25, 27, 25, 28, 30, 16, 42, 42, 27, 25, 16, 26…
$ to_link_end_10m <chr> "6", "6", "11", "27", "21", "29", "26", "8", "7", "92"…
$ link_ver        <int> 202100, 202100, 202100, 202100, 202100, 202100, 202100…
  • datetime: 時刻。5分単位で記録
  • source_code: 情報源コード
  • location_no: 計測地点番号
  • location_name: 計測地点名称
  • meshcode10km: 2次メッシュコード
  • link_type: リンク区分
  • link_no: リンク番号
  • traffic: 断面交通量。ある道路断面をある方向に通過する単位時間当たりの交通量(単位: 台)
  • to_link_end_10m: リンク終端からの距離
  • link_ver: リンクバージョン

元データ(CSV)のサイズ

断面交通量情報データを48ヶ月(2018年11月から2022年10月、4年)分収集したデータを使います。

全体のcsvファイルの大きさは.zip圧縮時で652Gとなります。

ベンチマークを測るために4つのデータを用意しました。なお、LargeHugeタイプでのcsvのファイルサイズは.zip圧縮時の大きさです。実際はこれより大きなサイズになることが予想されます。

# A tibble: 4 × 7
  type   area   period        nrow  ncol    csv_size parquet_size
  <chr>  <chr>  <chr>        <dbl> <int> <fs::bytes>  <fs::bytes>
1 Small  東京都 1ヶ月     19209389    10       1.59G      102.34M
2 Medium 東京都 1年      228012986    10      19.29G        1.17G
3 Large  全国   1年     3923767686    10      260.3G       13.49G
4 Huge   全国   4年    15677276121    10      643.2G        54.1G

実行速度の比較

処理1: csvの読み込み

Large, Hugeについてはcsvファイルを用意しなかったため、SmallとMediumでの結果を示します。

Small, Mediumともに読み込みの速度は{data.table}パッケージのfread()関数が最も高速でした。2つのデータでの順位は{data.table}、{arrow}、{readr}の順となりました。

task_input_small_res <- 
  pins_resources_local |> 
  pins::pin_read("benchmark_input_small")
task_input_small_res
             expression       min    median mem_alloc total_time
1       readr::read_csv 10.049957 10.225336    1.43GB  51.097654
2     data.table::fread  1.547197  1.736462    1.12GB   8.597198
3 arrow::read_csv_arrow  1.240628  2.500344  586.23MB  11.946824

task_input_medium_res <- 
  pins_resources_local |> 
  pins::pin_read("benchmark_input_medium")
task_input_medium_res
             expression       min    median mem_alloc total_time
1       readr::read_csv 246.88505 275.81730   18.69GB   823.9328
2     data.table::fread  19.06432  20.78965    14.3GB    69.3234
3 arrow::read_csv_arrow  42.42040  90.66747    7.65GB   244.6827

処理2: グループ化と集計

データへの操作を行う場合、{arrow}および{duckdb}を使うと他のパッケージよりも高速に処理できました。

task_gs_small_res <- 
  pins_resources_local |> 
  pins::pin_read("benchmark_task_gs_small")
task_gs_small_res
  expression       min    median mem_alloc total_time
1      dplyr 0.3428026 0.3536854  423.54MB  1.7569683
2 data.table 0.1811426 0.1880294   513.3MB  0.9335495
3     dtplyr 0.1821969 0.1902379  514.72MB  0.7620701
4      arrow 0.1020080 0.1045941    4.37MB  0.4167648
5     duckdb 0.1407175 0.1432536    8.49MB  0.5723605

task_gs_medium_res <- 
  pins_resources_local |> 
  pins::pin_read("benchmark_task_gs_medium")
task_gs_medium_res
  expression      min   median mem_alloc total_time
1      dplyr 4.957989 4.957989     5.4GB   4.957989
2 data.table 2.304412 2.304412    5.95GB   2.304412
3     dtplyr 4.903621 4.903621    5.95GB   4.903621
4      arrow 1.060302 1.060302  114.96KB   1.060302
5     duckdb 1.412729 1.412729  329.88KB   1.412729

処理3: 結合

結合処理ではSmallサイズのデータでは{dplyr}が最も高速に処理を終えましたが、Mediumサイズでは{arrow}が一番早い結果となりました。{duckdb}の利用が共通して遅い、という結果を示していますが、これはto_duckdb()関数の適用のタイミングの問題だと考えられます。タイミングを工夫することで{arrow}での処理速度に近づけることが期待できます。

task_join_small_res <- 
  pins_resources_local |> 
  pins::pin_read("benchmark_task_join_small")
task_join_small_res
  expression          min       median mem_alloc  total_time
1      dplyr  0.002996526  0.003111982  189.12KB  0.01662111
2 data.table  1.285227656  1.308420823    1.79GB  7.08349923
3     dtplyr  1.300879160  1.345852880    1.86GB  5.38343706
4      arrow  0.387688661  0.399215606  146.62MB  1.98544878
5     duckdb 14.483744542 14.792288935    1.65GB 73.62826925

task_join_medium_res <-
  pins_resources_local |>
  pins::pin_read("benchmark_task_join_medium")
task_join_medium_res
  expression       min     median mem_alloc total_time
1      dplyr  15.28164  15.415448      17GB   53.12222
2 data.table  19.70175  22.478590    23.8GB   68.23863
3     dtplyr  21.09285  21.533735    24.6GB   64.93516
4      arrow   5.65726   6.706522     1.7GB   20.40648
5     duckdb 123.75605 131.078867    10.2GB  412.29235