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

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

ctylim
November 28, 2022

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

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で書いたソフトウェアでサービスの価値を高めよう