CUBE SUGAR CONTAINER

技術系のこと書きます。

Apache Hive の Vectorization 機能を試す

今回は Apache Hive の Vectorization 機能を使ってパフォーマンスが向上するか試してみる。 Apache Hive では、通常 HDFS に保存されたデータを一行ずつ処理する。 それに対し Vectorization 機能を使うと、状況は限られるものの複数行をまとめて処理できるようになる。 結論から先に書くと、機能を有効にすることで多少のパフォーマンス向上はあることが分かった。

使った環境は次の通り。

$ cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core) 
$ uname -r
3.10.0-693.17.1.el7.x86_64
$ hive --version
Hive 2.3.2
Git git://stakiar-MBP.local/Users/stakiar/Desktop/scratch-space/apache-hive -r 857a9fd8ad725a53bd95c1b2d6612f9b1155f44d
Compiled by stakiar on Thu Nov 9 09:11:39 PST 2017
From source with checksum dc38920061a4eb32c4d15ebd5429ac8a
$ hadoop version
Hadoop 2.8.3
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r b3fe56402d908019d99af1f1f4fc65cb1d1436a2
Compiled by jdu on 2017-12-05T03:43Z
Compiled with protoc 2.5.0
From source with checksum 9ff4856d824e983fa510d3f843e3f19d
This command was run using /home/vagrant/hadoop-2.8.3/share/hadoop/common/hadoop-common-2.8.3.jar

ダミーデータを用意する

パフォーマンスを測るからには、それなりのデータ量が必要となるはずなのでダミーデータを生成する。 今回は Golang とフェイクデータを生成するパッケージの github.com/icrowley/fake を使った。

まずは Golang をインストールしてパッケージをダウンロードしておく。

$ sudo yum -y install golang git
$ go get github.com/icrowley/fake

以下のようなダミーデータ生成用のプログラムを用意した。

$ cat << 'EOF' > dummy_users.go
package main

import (
  "fmt"
  "strconv"
  "os"
  "math/rand"
  "time"
  "github.com/icrowley/fake"
)

func main() {
  rand.Seed(time.Now().UnixNano())

  if len(os.Args) < 2 {
    fmt.Println("Too few arguments")
    fmt.Printf("%s N\n", os.Args[0])
    os.Exit(1)
  }

  N, err := strconv.Atoi(os.Args[1])
  if err != nil {
    fmt.Printf("Please enter the integer: %s\n", N)
    os.Exit(1)
  }

  for i := 0; i < N; i++ {
    name := fake.FirstName()
    age := rand.Intn(100)
    fmt.Printf("%d,%s,%d\n", i, name, age)
  }
}
EOF

上記のプログラムをビルドする。

$ go build dummy_users.go 

実行すると ID と名前と年齢のダミーデータを CSV で出力する。

$ ./dummy_users 10
0,Gregory,74
1,Sharon,35
2,Beverly,22
3,Mark,25
4,Ralph,12
5,Douglas,63
6,Catherine,44
7,Joyce,84
8,Bruce,87
9,Kevin,3

とりあえず 1000 万件 (行) のデータを生成しておく。

$ ./dummy_users 10000000 > dummy_users.csv

次のように 168MB の CSV ができた。

$ wc -l dummy_users.csv 
10000000 dummy_users.csv
$ du -m dummy_users.csv 
168    dummy_users.csv

ダミーデータを Hive のテーブルに読み込む

続いてはダミーデータを Apache Hive のテーブルに読み込む。

まずは Hive の CLI を起動する。

$ hive

先ほど生成したダミーデータと一致するようなテーブルを作っておく。

hive> CREATE TABLE users (
    >   id BIGINT,
    >   name STRING,
    >   age INT
    > )
    > ROW FORMAT DELIMITED
    > FIELDS TERMINATED BY ','
    > STORED AS TEXTFILE;
OK
Time taken: 1.243 seconds

あとは先ほど作った CSV をテーブルに読み込むだけ。

hive> LOAD DATA LOCAL INPATH 'dummy_users.csv' OVERWRITE INTO TABLE users;
Loading data to table default.users
OK
Time taken: 12.993 seconds

ORC フォーマットのテーブルにコピーする

Apache Hive の Vectorization 機能は、当初は ORC ファイルフォーマットだけに対応していた。 ただし Apache Hive のバージョン 2.1 以降では、それ以外のフォーマットもサポートされているらしい。 とはいえ、軽く触ったところ ORC の方がパフォーマンス向上が望めそうなので、そちらを使うことにする。

[HIVE-12878] Support Vectorization for TEXTFILE and other formats - ASF JIRA

まずは、先ほどとスキーマは同じでファイルフォーマットだけ ORC に変更したテーブルを用意する。

hive> CREATE TABLE users_orc (
    >   id BIGINT,
    >   name STRING,
    >   age INT
    > )
    > ROW FORMAT DELIMITED
    > FIELDS TERMINATED BY ','
    > STORED AS ORC;
OK
Time taken: 0.089 seconds

あとは、元のテーブルからデータをコピーしてくるだけ。

hive> INSERT INTO TABLE users_orc SELECT * FROM users;
...
OK
Time taken: 47.313 seconds

Vectorization 機能を有効にしてみる

Vectorization 機能は hive.vectorized.execution.enabledhive.vectorized.execution.reduce.enabled で有効・無効を切り替える。 次のように、デフォルトでは無効化されている。

hive> set hive.vectorized.execution.enabled;
hive.vectorized.execution.enabled=true
hive> set hive.vectorized.execution.reduce.enabled;
hive.vectorized.execution.reduce.enabled=true

この項目を true にすれば機能が有効になる。

hive> set hive.vectorized.execution.enabled=true;
hive> set hive.vectorized.execution.reduce.enabled=true;

Vectorization 機能が使えない状況では単に設定が無視されるだけなので、とりあえず有効にしておいても良さそう。

機能の有無でパフォーマンスを比較する

Vectorization 機能は、比較的単純なクエリを実行するときに使えるらしい。 そこで、次のような年齢ごとのユーザ数を集計するクエリで比較してみよう。

hive> SELECT
    >   age,
    >   COUNT(1)
    > FROM users_orc
    > GROUP BY age;
...
0  100244
1  100033
2  99509
3  100067
4  99326
5  100026
...
95 99622
96 99675
97 100287
98 99751
99 100240

機能の切り替えを簡単にしたいので、設定を ~/.hiverc という設定ファイルに書き込むことにする。 ここに書いておくと Hive の CLI を起動するとき、自動でそこに記述されている内容を読み込んでくれる。

$ cat << 'EOF' > ~/.hiverc
set hive.vectorized.execution.enabled=false;
set hive.vectorized.execution.reduce.enabled=false;
EOF

まずは機能を無効にした状態で、次のように 10 回ほどクエリを実行したときの時間を測ってみた。

$ SQL='SELECT age, COUNT(1) FROM users_orc GROUP BY age'
$ for i in {1..10}; do hive -e "$SQL" 2>&1 | tail -n 1; done;
Time taken: 48.642 seconds, Fetched: 100 row(s)
Time taken: 46.033 seconds, Fetched: 100 row(s)
Time taken: 48.642 seconds, Fetched: 100 row(s)
Time taken: 47.711 seconds, Fetched: 100 row(s)
Time taken: 47.276 seconds, Fetched: 100 row(s)
Time taken: 48.677 seconds, Fetched: 100 row(s)
Time taken: 47.498 seconds, Fetched: 100 row(s)
Time taken: 46.391 seconds, Fetched: 100 row(s)
Time taken: 46.013 seconds, Fetched: 100 row(s)
Time taken: 48.593 seconds, Fetched: 100 row(s)

次は設定ファイルで機能を有効化する。

$ cat << 'EOF' > ~/.hiverc
set hive.vectorized.execution.enabled=true;
set hive.vectorized.execution.reduce.enabled=true;
EOF

先ほどと同じように実行時間を測ってみた結果が次の通り。 何だか、ほんのり速くなっているような?

$ for i in {1..10}; do hive -e "$SQL" 2>&1 | tail -n 1; done;
Time taken: 45.088 seconds, Fetched: 100 row(s)
Time taken: 43.822 seconds, Fetched: 100 row(s)
Time taken: 45.188 seconds, Fetched: 100 row(s)
Time taken: 45.157 seconds, Fetched: 100 row(s)
Time taken: 43.627 seconds, Fetched: 100 row(s)
Time taken: 45.167 seconds, Fetched: 100 row(s)
Time taken: 45.897 seconds, Fetched: 100 row(s)
Time taken: 45.647 seconds, Fetched: 100 row(s)
Time taken: 43.263 seconds, Fetched: 100 row(s)
Time taken: 44.603 seconds, Fetched: 100 row(s)

実行にかかった時間を比較する

なんだか、ほんのり速くなっているような感じはするけど確証がないので詳しく見ていくことにしよう。 これ以降の部分は、別に Apache Hive とは関係がないので、どういった環境を使っても構わない。

調査するために、まずは Python のサードパーティ製パッケージの scipynumpy をインストールしておく。

$ pip install scipy numpy

Python の REPL を起動する。

$ python

機能を有効にする前と後の実行時間を numpy の配列として用意する。

>>> import numpy as np
>>> before = np.array([
...     48.642,
...     46.033,
...     48.642,
...     47.711,
...     47.276,
...     48.677,
...     47.498,
...     46.391,
...     46.013,
...     48.593,
... ])
>>> after = np.array([
...     45.088,
...     43.822,
...     45.188,
...     45.157,
...     43.627,
...     45.167,
...     45.897,
...     45.647,
...     43.263,
...     44.603,
... ])

平均実行時間を比べると、機能を有効にしたときの方が 3 秒前後短いようだ。

>>> before.mean()
47.5476
>>> after.mean()
44.7459

ということで Vectorization 機能はパフォーマンスの向上に有効でした。 ...というのは、あまりにも短絡的なので、一応検定しておくことにする。

ウェルチの t 検定で、機能を有効にした方の平均が「有意に小さい」を対立仮説に「有意に小さくない」を帰無仮説として検定する。

>>> _, p = stats.ttest_ind(before, after, equal_var=False)
>>> p / 2
3.957745312666858e-06

p-value は非常に小さな値なので、仮に有意水準 1% でも余裕で帰無仮説は棄却され対立仮説を採用することになる。

最初、F 検定からの単純 t 検定を使っていたんだけど、どうやら最近は等分散とか関係なくウェルチの t 検定でええやんってことみたい。 ちなみに、最初の方法でも等分散かつ有意だった。

laboratoryofbiology.blogspot.jp

めでたしめでたし。

プログラミング Hive

プログラミング Hive

  • 作者: Edward Capriolo,Dean Wampler,Jason Rutherglen,佐藤直生,嶋内翔,Sky株式会社玉川竜司
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2013/06/15
  • メディア: 大型本
  • この商品を含むブログ (3件) を見る

統計学入門 (基礎統計学?)

統計学入門 (基礎統計学?)