推測するな、計測せよ。

2016ba6b977a2e6691811fa66d5f4336?s=47 CyberAgent
February 22, 2019

 推測するな、計測せよ。

CA BASE CAMP 2019
推測するな、計測せよ。
〜広告乱択アルゴリズムのボトルネックを探して〜
鳥越 貴智

2016ba6b977a2e6691811fa66d5f4336?s=128

CyberAgent

February 22, 2019
Tweet

Transcript

  1. 2.

    ⿃越 貴智 • アドテクスタジオ AMoAd 中途 3 年⽬ データ‧機械学習エンジニア •

    量⼦ゼミ ScalaMatsuri で登壇しました ( ・ㅂ・)و 早稲⽥の⽥中宗先⽣とアニーリングの話をしてます>< piyo piyo z
  2. 4.

    • CyberAgent と DeNA の合弁会社 2011 年 4 ⽉に設⽴ 開発はアドテクスタジオ

    アドネットワーク専業 • 多彩な広告フォーマット AMoAd ネットワーク AMoAd インフィード AfiO(動画) D AD
  3. 10.

    確率分布 • ⼀様分布 :0 以上 1 以下の値が均等に出現 • ベータ分布:0 以上 1

    以下の値が偏って出現(偏り⽅のパラメータがある)
  4. 11.

    do { final double u1 = random.nextDouble(); final double u2

    = random.nextDouble(); final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1)); w = a * FastMath.exp(v); final double z = u1 * u1 * u2; r = gamma * v - 1.3862944; final double s = a + r - w; if (s + 2.609438 >= 5 * z) { break; } t = FastMath.log(z); if (s >= t) { break; } } while (r + alpha * (FastMath.log(alpha) - FastMath.log(b + w)) < t); ベータ分布のサンプリング実装
  5. 13.

    do { final double u1 = random.nextDouble(); final double u2

    = random.nextDouble(); final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1)); w = a * FastMath.exp(v); final double z = u1 * u1 * u2; r = gamma * v - 1.3862944; final double s = a + r - w; if (s + 2.609438 >= 5 * z) { break; } t = FastMath.log(z); if (s >= t) { break; } } while (r + alpha * (FastMath.log(alpha) - FastMath.log(b + w)) < t); ベータ分布のサンプリング実装
  6. 16.

    ஆ ͔ ͘ ݟ क Δ ࣄ ۀ ੹ ೚

    ऀ ू ܭ Λ վ म ͠ ͯ ͘ Ε ͨ ಉ ྅ ʮ͋ɺ͜Εແཧ͔΋ʯͱ਒͑Δࢲ
  7. 18.

    性能要件 • ⼀般的にアドテクの許容 Latency はトータル 100ms 以内が⽬安 • 当然その⼀部であるマイクロサービスの許容 Latency

    はもっと短い • 現状ピーク時間帯 1500 qps の 95% を Latency ms 以内でさばいている • Latency ms 以内を要件に
  8. 19.

    負荷テスト . テスト環境に広告選択マイクロサービス (Finagle) を⽴てて本番 DB に繋ぐ . 本番ログからテスト⽤シナリオを⽣成する .

    Gatling で負荷をかけて response time を計測する . Finagle のプロファイリング⽤エンドポイントを叩く
  9. 20.

    計測結果(チューニング前) • Gatling で計測した response time は明らかに性能要件を満たせてない • Finagle のプロファイルを⾒ると、

    • BetaDistribution は呼んでるけど、 • AbstractWell とか Well c (AbstractWell の⼦クラス) とか覚えない…… $ pprof -cum —text profile | grep math3 … 29.3% org.apache.commons.math3.distribution.BetaDistribution. … 29.3% org.apache.commons.math3.random.Well19937c. … 29.3% org.apache.commons.math3.random.AbstractWell. … 29.3% org.apache.commons.math3.random.AbstractWell.setSeed
  10. 21.

    ボトルネックのコード AbstractWell.java からコードを抜粋 • JDK の System.identityHashCode のバグを踏んでいるっぽい JDK- :

    Performance problem with System.identityHashCode in client compiler public void setSeed(final int[] seed) { if (seed == null) { setSeed(System.currentTimeMillis() + System.identityHashCode(this)); return; }
  11. 23.

    改修コード • Java 標準の ThreadLocal で⼀様乱数⽣成器をスレッドローカル変数化 class PickStage { val

    threadLocalRng: ThreadLocal[Well19937c] = ThreadLocal.withInitial(() => new Well19937c()) …… def sampleBetaDistribution(alpha: Double, beta: Double): Double = new BetaDistribution(threadLocalRng.get, alpha, beta).sample() }
  12. 26.
  13. 28.
  14. 32.

    これまで遭遇したボトルネック • 配列に⼤量の要素を⼀つずつ追加 ➡ 事前にメモリ確保 • スパースな巨⼤多重配列の確保 ➡ 連想配列に置きかえ • コンストラクタの引数で巨⼤なコピー ➡ 右辺値参照渡し • 多重ループで結合 ➡ マージ結合に切りかえ •

    巨⼤な⾏列演算 ➡ 線形代数ライブラリに投げる • 全ワーカーが全マスターデータ取得 ➡ データをID分割 • 膨⼤な中間ファイル読み込み ➡ 中間出⼒時にパーティションを纏める • リソースロックのリトライが衝突しつづける ➡ 乱数で揺らぎを⼊れる
  15. 33.

    これまで遭遇したボトルネック • 配列に⼤量の要素を⼀つずつ追加 ➡ 事前にメモリ確保 • スパースな巨⼤多重配列の確保 ➡ 連想配列に置きかえ • コンストラクタの引数で巨⼤なコピー ➡ 右辺値参照渡し • 多重ループで結合 ➡ マージ結合に切りかえ •

    巨⼤な⾏列演算 ➡ 線形代数ライブラリに投げる • 全ワーカーが全マスターデータ取得 ➡ データをID分割 • 膨⼤な中間ファイル読み込み ➡ 中間出⼒時にパーティションを纏める • リソースロックのリトライが衝突しつづける ➡ 乱数で揺らぎを⼊れる ਪ ଌ ෆ ೳ