Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

FINAL FANTASY Record Keeperを支えたGolang

24,195 views

Published on

FINAL FANTASY Record Keeper用に作ったツールのGolang実装についていろいろ。

Published in: Technology
  • Be the first to comment

FINAL FANTASY Record Keeperを支えたGolang

  1. 1. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. GoCon 2015 Winter FINAL FANTASY Record Keeperを支えた Golang 2015/12/06 GDI 渋川よしき
  2. 2. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. お前だれよ?  前職 ⁃ 自動車会社の社内SE  現職 ⁃ 社内ゲームエンジン用のフレームワーク (クライアント用もサーバ用も)作ったり、 開発支援ツール作ったりいわゆる社内SE ⁃ たまにゲーム開発を手伝ったりもします。 渋川 よしき  プログラミング  C++とかPythonとかGolangとかJavaScriptとか  本  つまみぐい勉強法、アート・オブ・コミュニティ(翻訳)、 Mobageを支える技術、オブジェクト指向JavaScript(翻訳)、 ポモドーロ・テクニック入門(翻訳)、etc
  3. 3. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. ©SQUARE ENIX CO., LTD. ©DeNA Co., Ltd.
  4. 4. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 前回までのあらすじ
  5. 5. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. FFRKマスターデータ運用改善 http://www.slideshare.net/dena_study/final-fantasy-record-keeper
  6. 6. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. まとめ: どういうところで使ったか?  ゲームのマスターデータ ⁃ マスターデータというのはプログラムでも絵でも音でもない要素 ⁃ キャラクタや敵や技のパラメータ、ダンジョンの難易度、 シナリオの文言、チュートリアル設計etc ⁃ 3Dの世界になると、レベルデザイナーなどさらに分業化している が、DeNAでは3つの役割に分担している • エンジニア: プログラム • アーティスト: 絵とかアニメーションとかUIデザイン • プランナー: マスターデータ  Google Drive上にあるスプレッドシートに入ったマスターデータの元 データからゲームサーバが使えるフォーマットに変換するツールを Golangで書いたら、Google Apps Scriptよりも100倍早くなったよ 今日は実装部分の話をします
  7. 7. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. ラフな全体像 ファイル一覧 のシート取得 ファイル一覧で指定 された名前のファイル のキーのリストを作成フォルダの全子要素の キーと名前を取得 ファイルをダウンロード ファイルをロードし、 JSONSheet / JSONFormatを分析 •RefListTemplateを元に 値の置き換え •CSV出力  Jenkins上で実行されるコマンドラインプログラム ⁃ 開発者の手元でも実行できる  他にもいくつかサポートのプログラムが何本かいる(差分レポート作成 とか) 同名のシートで グループ化
  8. 8. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 今日のレシピ  使っている開発環境・ライブラリ  並列処理の実装と並列処理のログ出力  Google APIのアクセス頻度を制御する  diff(文字単位、行単位、blameの実装)  xlsxファイルの読み込みと高速化
  9. 9. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 使っている開発環境・ライブラリ
  10. 10. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 使っている環境・ライブラリ  IntelliJ IDEA + golang-idea-plugin ⁃ https://github.com/go-lang-plugin-org/go-lang-idea-plugin ⁃ VimはなるべくデフォルトにしておきたいのでIDE ⁃ pluginはどんどん改善されたりしている(が不安定な部分も) ⁃ 英語のスペルチェッカーが地味便利  Sphinx ⁃ ドキュメントツール (http://sphinx-users.jp) ⁃ 使い方を説明する4択クイズも作れる!(作った)  github.com/jessevdk/go-flags (MIT) ⁃ コマンドライン引数のパース ⁃ short/long、同じ引数を複数回指定、きれいなヘルプテキスト出力 あたりが良かった ⁃ 構造体のタグで指定する • 多言語化しにくいという欠点があるので言語ごとに構造体を作って切り替 えという苦肉の策
  11. 11. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
  12. 12. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  github.com/google/google-api-go-client (MIT) ⁃ Google APIにアクセスするためのライブラリ  github.com/tealeg/xlsx (BSD style) ⁃ xlsxを読み書きできるPure Goライブラリ  github.com/sergi/go-diff (MIT/オリジナルはAPL v2) ⁃ Googleの Diff, Match and PatchライブラリのGolang移植  github.com/bkaradzic/go-lz4 (BSD) ⁃ 高速なファイル圧縮・展開ライブラリのPure Go版  github.com/OneOfOne/xxhash (APL v2) ⁃ lz4内でも使われている高速ハッシュアルゴリズムのPure Go実装。  github.com/ugorji/go/codec (MIT) ⁃ msgpack/jsonとかいろいろなシリアライズのライブラリ
  13. 13. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  github.com/mattn/go-colorable (MIT) ⁃ 信頼と安心のmattnプロダクツ ⁃ Windowsでも他の環境でもコンソール出力に色が付く!  github.com/mgutz/ansi (MIT) ⁃ mattnプロダクツと一緒に使える色付け用の便利関数  github.com/jteeuwen/go-bindata (Public Domain) ⁃ バイナリの中にファイルを入れちゃうツール ⁃ HTMLテンプレートとgoogle APIアクセストークン、デフォルト値 のJSONあたりを連結してます
  14. 14. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  golang.org/x/oauth2 ⁃ OAuth2  golang.org/x/text/unicode/norm ⁃ ユニコードの正規化(NFDからNFC)関連 ⁃ Mac OS Xを嫌いになる前に  github.com/xeipuuv/gojsonschema (APL v2) ⁃ JSONスキーマのバリデーションライブラリ ⁃ ツールが戦魂チームで魔改造されたときに追加されてた  github.com/nicksnyder/go-i18n (MIT) ⁃ 国際化するためのライブラリ ⁃ JSONで翻訳を指定する。単数・複数で訳文の使い分けもできる ⁃ http://qiita.com/shibukawa/items/f0e4df597e62372fe7d5
  15. 15. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 並列処理の実装とログ出力
  16. 16. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 並列処理  汎用のスレッドプールを作ってみて、大量アクセス時はそれだけを使っ ている ⁃ Google Driveでフォルダの中の情報のアクセス ⁃ xlsxのダウンロード ⁃ xlsxの読み込み ⁃ 同名のシートごとにデータ分析・変換 ⁃ 同名のシートごとにCSV/JSON/TSV/HTML等で出力  ジョブを途中から追加できるようにして貪欲的に処理を行えるようにし たがその機能は使わず ⁃ OOMキラーに殺されまくったので、メモリ使用量の削減のために 処理を適度の中断してそれぞれの段階で枝刈りを積極的に行う方向 に方針転換
  17. 17. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  使っているところのイメージ この中の処理を全タスクに対して適当にスレッドプールを使って 並列で処理する これがジョブ一覧の入ったキュー
  18. 18. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
  19. 19. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
  20. 20. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 並列処理のログ出力  そのまま出力しても順序が混ざってしまって読みにくくなってしまう  グループ情報付きのエラー出力関数を作って、一旦メモリに貯めておい てグループごとに分類して出力するようにした。  APIアクセスエラーとか、入力ファイル名間違いなどの致命的な問題は log.Fatal()するが、読み込んだ後は基本的に最後まで完走する設計にし ている。  コンソールに出力したらJenkinsがそのまま表示してくれるので出しっ ぱなし。JenkinsのText FinderプラグインでWARNINGの文字を見つけ たら不安定(Unstable)扱いにしている。 グループ
  21. 21. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. Google APIのアクセス制御  ユーザごとの秒間アクセス数をGoogle Developer Consoleで設定して も、それより大分下のアクセス頻度で403 User Rate Limit Exceeded が出る件 ⁃ 昔は数千でも設定できたけど、最近10回/秒までしか増やせなく なったっぽい。ちなみに10億/日なのでフルにアクセスしても 0.1%しか行かない!
  22. 22. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  time.Tickerを使って、アクセス頻度の制御を行うようにした  APIアクセスする関数(スレッドプールで並列で動いていても)の中で、 下記のAPIThrottling()関数を呼び出すと、呼び出しが適当に間引かれ て実行されるようになった
  23. 23. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. diffの実装(文字単位/blameの実装)
  24. 24. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. Diff, Match, Patch  Google製のライブラリ。基本的にはこれを使っておけば間違いない ⁃ https://neil.fraser.name/software/diff_match_patch/svn/trun k/demos/demo_diff.html  このライブラリの基本設計としては、まずできるだけ細かい文字単位 diffを計算して、その後適度な粒度になるようにパラメータをチューニ ングしながらクリーンナップするという2段階で実行する方式のAPIに なっている  なんというかクセがあるからサンプルに従うのが良さそう 今日私は朝ごはんを食べました。 明日私はお酒を飲みに行きます。 今明日私は朝ごはんお酒を 食べ飲みに行きましたす。 今明日私は朝ごはんを食べまし たお酒を飲みに行きます。
  25. 25. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 使い方  文字単位diff  セマンティックに従ってdiffをマージ  行単位diff ⁃ http://qiita.com/shibukawa/items/dd75ad01e623c4c1166b before := “今日私は朝ごはんを食べました。” after := “明日私はお酒を飲みに行きます。” dmp := diffmatchpatch.New() result := dmp.DiffMain(before, after, true) dmp := diffmatchpatch.New() tmp := dmp.DiffMain(before, after, true) result := dmp.DiffCleanupEfficiency(tmp)
  26. 26. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  最終的な出力はdiffmatchpatch.Diff構造体の配列 ⁃ テキストと、操作(そのまま、追加、削除)の情報を持つ  Diff構造体配列を抜き出して見やすい出力にする ⁃ ↓変化しない項目と、削除された項目だけを表示。削除された項目 には text-decoration: line-through; と赤色を付ける。 ⁃ ↑変化しない項目と、追加された項目だけを表示。追加された項目 には緑色を付ける
  27. 27. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. diffの応用: blameコマンド  スプレッドシートでもgit blameしたくなるってことあるよね!  シートの行ごとに、最終編集者と変更日時を表示させることも可能です
  28. 28. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. blameの実装 1. まずはGoogle Driveにアクセスして落とせるだけの全リビジョンのス プレッドシートをxlsx形式で落とします。 2. 新・旧ファイルをCSV化して、行区切りの文字列配列を作ります ⁃ (実際はオンメモリ処理です, 説明のため1シートだけ扱います) 1000, “たたかう”, “敵1体を通常攻撃” 1001, “ケアルガ”, “対象のHPを回復” 1003, “ファイラ”, “炎属性魔法攻撃(効果: 小) 1000, “たたかう”, “敵1体を通常攻撃” 1001, “ケアルガ”, “対象のHPを回復” 1003, “ファイラ”, “炎属性魔法攻撃(効果: 中) 1000, “たたかう”, “敵1体を通常攻撃” 1001, “ケアルガ”, “対象のHPを回復” 1002, “ファイア”, “炎属性魔法攻撃(効果: 小) 1003, “ファイラ”, “炎属性魔法攻撃(効果: 中)
  29. 29. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 3. 古い方からの2つ取り出して行単位差分を取ります 3. 追加されていればその行は新(index: 1)で追加された、維持であれば旧 (index: 0)で更新されたとみなし(削除は無視)、その行がどのファイル 由来のものかを記憶していく 4. 新しいファイルと、その次のファイルを取り出し、同じように行のイ ンデックスを更新していく。これを最後のファイルまで続けていく 1000, “たたかう”, “敵1体を通常攻撃” 1001, “ケアルガ”, “対象のHPを回復” 1003, “ファイラ”, “炎属性魔法攻撃(効果: 小) 1003, “ファイラ”, “炎属性魔法攻撃(効果: 中) 0: 1000, “たたかう”, “敵1体を通常攻撃” 0: 1001, “ケアルガ”, “対象のHPを回復” 1003, “ファイラ”, “炎属性魔法攻撃(効果: 小) 1: 1003, “ファイラ”, “炎属性魔法攻撃(効果: 中) 0: 1000, “たたかう”, “敵1体を通常攻撃” 0: 1001, “ケアルガ”, “対象のHPを回復” 2: 1002, “ファイア”, “炎属性魔法攻撃(効果: 小) 1: 1003, “ファイラ”, “炎属性魔法攻撃(効果: 中)
  30. 30. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. xlsxの読み込みと高速化
  31. 31. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. xlsxのロード  github.com/tealeg/xlsxがオンリーワンの選択肢。これでOK? ⁃ ではなかった。 • ポインタで持つべき所を実体で大量に持ってメモリを食っていたり、構造 体のメンバーが初期化されてないところがあったり、先頭の空行が勝手に 省略されて行がずれてたり、生成されたxlsxファイルがExcelで読み込めな かったり・・・ ⁃ バグ修正のPull Requestを送り、ついでにセルのタイプ(数値型や Boolean型など)を取得できるように機能拡張もした。 ⁃ 今もたくさんユーザがいて、スタイルなども扱えるように修正され 続けているし、基本的に良いライブラリです ⁃ xlsxのライブラリで困ったらPythonのopenpyxlを参照すると良い です。MS製のXMLスキーマを使ったユニットテストもしていて、 困ったときのリファレンス実装として役立ちます。
  32. 32. Copyright (C) DeNA Co.,Ltd. All Rights Reserved.  しかし、xlsxのパースは重い ⁃ xlsxのロードがツールのボトルネックに(90%以上の時間) ⁃ プロファイラで計測しても、標準ライブラリの中ばかり・・・ • zipの展開は重い • XMLのパースは重い • XMLタグ構造体のインスタンス作成が重い • golangの構造体にマッピングするencoding/xmlは遅い? ⁃ タグのパースなどを事前にやることで高速だよ!というJSONパーサも あるぐらい • ついでにxlsxライブラリがセルごとに構造体を作っちゃうのも遅い
  33. 33. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 軽量xlsx的なモジュールを作成  APIはtealeg/xlsxとは少し違うけどなるべく同じに ⁃ セルは構造体を作らず行のオブジェクトに配列でデータを持たせる  github.com/ugorji/go/codecを使ってファイルとの読み書きをする ⁃ これだけでmsgpack, binc, cbor, jsonの読み書きができる ⁃ サイズを減らすためにlz4で圧縮もかけた
  34. 34. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 処理時間の推移  オリジナルのxlsxのまま読み書き ⁃ ダウンロードをキャッシュ済みの場合でも40秒ほど  JSON + lz4で圧縮のキャッシュ化 ⁃ スタイル情報とかもなくなるのでファイルサイズも1/2〜1/5 ⁃ メモリ使用量も1/3ぐらいに ⁃ 処理時間は15秒ぐらいに  MessagePack + lz4で圧縮のキャッシュ化 ⁃ ファイルサイズはJSON+lz4とほぼ同じ ⁃ 処理時間は6秒ぐらいに
  35. 35. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. まとめ  Excel/Google Spreadsheetを中心としたテキストの分析・加工のコー ドならGoでガンガン書ける ⁃ 表計算は非プログラマとのコミュニケーションツール • 読み書きソロバンExcelの時代 ⁃ 使える人は多いし、Excelの読み書きができれば最速ではないかも しれないけど90%のビジネスは回る ⁃ Goは速いので業務上のボトルネックを全力で叩き潰すのに便利 ⁃ 文字列中心の処理でもC++ほどやる気が消耗しない  Pure Goのライブラリも数多くでてきて、言語の後方互換性も高いので、 使える資産がどんどん増えている ⁃ リファレンスしかなくて使い方が良くわからないライブラリも多い ので、Qiitaとかでどんどん発信しましょう ⁃ まだまだ成熟してないライブラリも多いので、いっぱいPR出そう

×