テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(writeboost report) unlikelyパスに入って性能が悪くなるテストを考える

以下のunlikelyパスに入れて性能の劣化を計測したい. 色々とテストをしているので, 通ってないこともないだろうが, 意図的に定常的に通しまくることはなかなか難しい. 一つだけ方法を思いついたので, コードの説明がてらメモることとした.

何をやっているか?ライトブーストはログ書きする. SSDにある以前のログデータは書換えない(現在作成のログはオーバーライトする). 「あるデータを書こうとして, そのキャッシュを探したら見つかってしまった. もし, 現存するキャッシュがパーシャルで, 今書くものもパーシャルだったりすると, 過去のものをライトバックしましょう」というのがこのunlikelyパスに入る判断である.

過去のデータと書き込みデータをマージすれば良いじゃないかという人がいるかも知れないので言っておくと, それをやってしまうと, 過去すべてのデータをチェックする必要が出てくる. だから, 単にライトバックしている. これは頭が超よくないと読み取れないのでコメントした方がいいかも知れない.

unlikelyと言ってるのは, SSD上データも書き込みデータもパーシャルというケースは, ふつうはないし, あったとしたら上位でそれほど性能を考えていないから, 別にライトブーストが頑張る必要もないという考え. 例えば, シャットダウン時にメタデータを更新するとか, 数秒に1回更新されるスーパーブロック情報みたいなものとか, そういうものに性能を稼ぐのではなく, 1秒に何万回も飛んでくる典型的なIOに注力すべきということ. ここで仮に頑張れる手があったとして悪手となる. 切るものを切らないと, ソフトウェアも会社もダメになる.

dirty_bitsだけでなく, バックグラウンドのライトバックデーモンにすでにライトバックされてる場合もライトバックする必要がないというのも, unlikely度を高めている.

void invalidate_previous_cache(struct wb_device *wb, struct segment_header *seg,
                   struct metablock *old_mb, bool overwrite_fullsize)
{
    u8 dirty_bits = read_mb_dirtiness(wb, seg, old_mb);

    /*
    * First clean up the previous cache and write back the cache if needed.
    */
    bool needs_cleanup_prev_cache =
        !overwrite_fullsize || !(dirty_bits == 255);

    /*
    * Writeback works in background and may have cleaned up the metablock.
    * If the metablock is clean we need not to write back.
    */
    if (!dirty_bits)
        needs_cleanup_prev_cache = false;

    if (overwrite_fullsize)
        needs_cleanup_prev_cache = false;

    if (unlikely(needs_cleanup_prev_cache)) { // ここ
        wait_for_flushing(wb, seg->id);
        writeback_mb(wb, seg, old_mb, dirty_bits, true);
    }

    cleanup_mb_if_dirty(wb, seg, old_mb);

    ht_del(wb, old_mb);
}
static void prepare_write_pos(struct wb_device *wb, struct bio *bio,
                  struct write_job *pos)
{
    struct lookup_result res;

    mutex_lock(&wb->io_lock);

    /*
    * For design clarity, we insert this function here right after mutex is taken.
    * Making the state valid before anything else is always a good practice in the
    * in programming.
    */
    might_queue_current_buffer(wb, bio);

    cache_lookup(wb, bio, &res);

    if (res.found) {
        if (unlikely(res.on_buffer)) {
            /*
            * Overwrite on the buffer
            */
            pos->plog_head = advance_plog_head(wb, bio);
            pos->mb = res.found_mb;
            mutex_unlock(&wb->io_lock);
            return;
        } else {
            /*
            * Cache hit on the cache device.
            * Since we will write new dirty data to the buffer
            * we need to invalidate the existing thus hit cache block
            * beforehand.
            */
            invalidate_previous_cache(wb, res.found_seg, res.found_mb,
                          io_fullsize(bio));
            dec_inflight_ios(wb, res.found_seg);
        }
    }

dmtsに, このパスに入りまくるテストを提供したいのでどうしようか考えているわけだが, 例えば,

  • HDDは4KB * 127しかない.
  • SSDは1MB + 4KB * 127 * 2ある.

という状況で, 自動ライトバックを止めて1sector randwriteを打ちまくったらどうなるかというと, パーシャルだらけで穢れてログがフラッシュ. 次のログに書く時に前のログに全ヒット. そして全部unlikelyという感じにならないだろうか?最初のログに書く時だけは早いが, それ以降は常に前のログのライトバックをしないといけないからずっとやれば遅いパスが目立つ. たぶんHDDオンリーの場合より遅くなる.

その上, 新しいログを確保するためにライトバックデーモンが強制ライトバックしにいった時にはすでにライトバックが完了しているから, そのパスによるライトバックはない. つまり雑音が少ない(SSD/HDDが絡むことはなく, CPU-RAMで閉じる)

unlikelyを通りまくってることを検査することはprint文でもしこめば簡単なのでよしとして, テストについては数時間はかかるだろうから, もう少し頭の中で寝かせて, OKと思ったら一気にやる.