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

巨大なサイズのファイルの行をシャッフルする

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.
Avatar for ctylim ctylim
November 28, 2022

 巨大なサイズのファイルの行をシャッフルする

Avatar for ctylim

ctylim

November 28, 2022
Tweet

More Decks by ctylim

Other Decks in Programming

Transcript

  1. 自己紹介 ctyl 本名:四方 (シカタ) GitHub @ ctylim UNICORN株式会社 ソフトウェアエンジニア 2018/11〜

    主な業務:web広告入札アルゴリズム(RTB・検索型)・インフラコスト節約 社内Rust活用実績:広告価値予測のための学習ロジック(Pure Rust)・CLIツール等 好き:日本酒・旅行・競技プログラミング 2
  2. 先行ソフトウェア:terashuf terashuf (https://github.com/alexandres/terashuf) • C++製 • 昔社内でこれを使っていた • ファイル入出力のみ •

    (当時)CSVヘッダ行飛ばし等のオプションなし • (当時)厳格なrandom shuffleではない → 厳格な:全ての (i, j) に対して、i 行目がシャッフル後                    j 行目に移動する確率が 1/総行数 5
  3. Fisher-Yates shuffle 配列をランダムに並べ替えるアルゴリズム 配列長 N に対して時間・空間計算量 O(N) 7 pub fn

    fisher_yates_shuffle_n(n: usize) -> Vec<usize> { let mut v: Vec<usize> = Vec::with_capacity(n); for i in 0..n { v.push(i); } let mut rng = thread_rng(); for i in (1..n).rev() { let r: usize = rng.gen(); v.swap(i, r % (i + 1)); } v }
  4. 一時ファイルと組み合わせた大規模シャッフル① 8 1. メモリに入る分まで読んでシャッフル 2. 一時ファイルに書き出し を繰り返す … 100GB CSV

    4GB 4GB let file = NamedTempFile::new()?; let shuf_indices = fisher_yates_shuffle_n(rows.len()); let mut tmp_writer = io::writer(file.path().to_str()?; for i in shuf_indices { tmp_writer.write(format!("{}", rows[i]).as_bytes())?; } tmp_files.push(TmpFile { remaining_rows: rows.len(), file: file, }; total_rows += rows.len();
  5. 一時ファイルと組み合わせた大規模シャッフル② 9 TempFile 1 TempFile 2 TempFile 3 残り100行 残り100行

    残り50行 出力 1行読み進める確率 1行書き込む 重み付きランダムにファイルを 選択し1行読み進めることで、 全行のrandom shuffleを実現
  6. 一時ファイルと組み合わせた大規模シャッフル let mut rng = thread_rng(); for i in 0..total_rows

    { let r: usize = rng.gen_range(0, total_rows - i) + 1; let mut rows = 0; for j in 0..tmp_files.len() { rows += tmp_files[j].remaining_rows; if r <= rows { let mut buf = String::new(); read_line(&mut tmp_file_readers[j], &mut buf)?; writer.write(format!("{}", buf).as_bytes())?; tmp_files[j].remaining_rows -= 1; break; } } } 10 残り行数に対する 重み付きランダムで 一時ファイルを選ぶ 1行読み進める ※内側のforループはBinary Indexed Treeで高速化可能
  7. 結果 速度計測 (メモリ 16GB, 一時ファイルサイズ 4GB) 使用感 • (わかってはいたけど) Disk

    IOがボトルネック ◦ 出力時ファイル分割・並列化 など... • ワークフロー全体だとシャッフル前後の処理がボトルネックになる ◦ データのクエリ・整形等 11 Software real user sys GNU shuf 0m59s 0m34s 0m14s terashuf 5m06s 4m43s 0m14s rhuffle 1m56s 1m06s 0m40s Software real user sys GNU shuf x x x terashuf 8m12s 7m16s 0m31s rhuffle 1m47s 0m39s 0m51s 5.3GB, 5500万行 9.0GB, 2200万行
  8. 業務でRustを使った開発をするには... Rustは大半の企業で事業のコアを担う開発言語として道半ば >>> これから使われるようになる <<< 条件: • 高速に動作する・メモリ効率・安全性に優れたシステム要請 • Rustを書くことに積極的な人が社内に多い

    CLIツール(→マイクロサービス)→プロジェクト と活用範囲を広げる 何より生産性の高いソフトウェアを書くことが大切... 12 Rustで書いたソフトウェアでサービスの価値を高めよう