Upgrade to Pro — share decks privately, control downloads, hide ads and more …

推測するな、計測せよ。

Avatar for CyberAgent CyberAgent
February 22, 2019

 推測するな、計測せよ。

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

Avatar for CyberAgent

CyberAgent

February 22, 2019
Tweet

More Decks by CyberAgent

Other Decks in Technology

Transcript

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

    量⼦ゼミ ScalaMatsuri で登壇しました ( ・ㅂ・)و 早稲⽥の⽥中宗先⽣とアニーリングの話をしてます>< piyo piyo z
  2. • CyberAgent と DeNA の合弁会社 2011 年 4 ⽉に設⽴ 開発はアドテクスタジオ

    アドネットワーク専業 • 多彩な広告フォーマット AMoAd ネットワーク AMoAd インフィード AfiO(動画) D AD
  3. 確率分布 • ⼀様分布 :0 以上 1 以下の値が均等に出現 • ベータ分布:0 以上 1

    以下の値が偏って出現(偏り⽅のパラメータがある)
  4. 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. 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. ஆ ͔ ͘ ݟ क Δ ࣄ ۀ ੹ ೚

    ऀ ू ܭ Λ վ म ͠ ͯ ͘ Ε ͨ ಉ ྅ ʮ͋ɺ͜Εແཧ͔΋ʯͱ਒͑Δࢲ
  7. 性能要件 • ⼀般的にアドテクの許容 Latency はトータル 100ms 以内が⽬安 • 当然その⼀部であるマイクロサービスの許容 Latency

    はもっと短い • 現状ピーク時間帯 1500 qps の 95% を Latency ms 以内でさばいている • Latency ms 以内を要件に
  8. 負荷テスト . テスト環境に広告選択マイクロサービス (Finagle) を⽴てて本番 DB に繋ぐ . 本番ログからテスト⽤シナリオを⽣成する .

    Gatling で負荷をかけて response time を計測する . Finagle のプロファイリング⽤エンドポイントを叩く
  9. 計測結果(チューニング前) • 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. ボトルネックのコード 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. 改修コード • 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. これまで遭遇したボトルネック • 配列に⼤量の要素を⼀つずつ追加 ➡ 事前にメモリ確保 • スパースな巨⼤多重配列の確保 ➡ 連想配列に置きかえ • コンストラクタの引数で巨⼤なコピー ➡ 右辺値参照渡し • 多重ループで結合 ➡ マージ結合に切りかえ •

    巨⼤な⾏列演算 ➡ 線形代数ライブラリに投げる • 全ワーカーが全マスターデータ取得 ➡ データをID分割 • 膨⼤な中間ファイル読み込み ➡ 中間出⼒時にパーティションを纏める • リソースロックのリトライが衝突しつづける ➡ 乱数で揺らぎを⼊れる
  13. これまで遭遇したボトルネック • 配列に⼤量の要素を⼀つずつ追加 ➡ 事前にメモリ確保 • スパースな巨⼤多重配列の確保 ➡ 連想配列に置きかえ • コンストラクタの引数で巨⼤なコピー ➡ 右辺値参照渡し • 多重ループで結合 ➡ マージ結合に切りかえ •

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