17

Linux 非同期 I/O システム コールを使用するライブラリを作成していますio_submitが、ext4 ファイル システムで関数のスケーリングが不十分な理由を知りたいです。可能であれば、io_submit大きな IO 要求サイズでブロックされないようにするにはどうすればよいですか? 私はすでに次のことを行っています(ここで説明されているように):

  • を使用しO_DIRECTます。
  • IO バッファーを 512 バイト境界に合わせます。
  • バッファー サイズをページ サイズの倍数に設定します。

カーネルが でどれくらいの時間を費やすかを観察するために、 と を使用しio_submitて 1 Gb のテスト ファイルを作成し、システム キャッシュ ( ) を繰り返しドロップして、ファイルのより大きな部分を読み取るというテストを実行しました。反復ごとに、読み取り要求が完了するまでにかかった時間と待機に費やされた時間を出力しました。カーネル バージョン 3.11 の Arch Linux を実行している x86-64 システムで、次の実験を実行しました。マシンには SSD と Core i7 CPU が搭載されています。最初のグラフは、読み取られたページ数と終了までの待機時間の関係を示しています。2 番目のグラフは、読み取り要求が完了するまでの待機に費やされた時間を表示します。時間は秒単位で測定されます。dd/dev/urandomsync; echo 1 > /proc/sys/vm/drop_cachesio_submitio_submit

ここに画像の説明を入力

ここに画像の説明を入力

比較のために、 を使用して同期 IO を使用する同様のテストを作成しましたpread。結果は次のとおりです。

ここに画像の説明を入力

非同期 IO は、約 20,000 ページの要求サイズまで期待どおりに機能しているようです。後はio_submitブロック。これらの観察は、次の疑問につながります。

  • の実行時間がio_submit一定でないのはなぜですか?
  • この不適切なスケーリング動作の原因は何ですか?
  • ext4 ファイル システム上のすべての読み取り要求を、それぞれのサイズが 20,000 ページ未満の複数の要求に分割する必要がありますか?
  • この「魔法の」値である 20,000 はどこから来たのでしょうか? 自分のプログラムを別の Linux システムで実行する場合、スケーリング動作が低下することなく、使用する最大 IO 要求サイズを決定するにはどうすればよいですか?

非同期 IO のテストに使用されるコードは次のとおりです。関連があると思われる場合は、他の情報源のリストを追加できますが、関連があると思われる詳細のみを投稿しようとしました.

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <chrono>
#include <iostream>
#include <memory>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
// For `__NR_*` system call definitions.
#include <sys/syscall.h>
#include <linux/aio_abi.h>

static int
io_setup(unsigned n, aio_context_t* c)
{
    return syscall(__NR_io_setup, n, c);
}

static int
io_destroy(aio_context_t c)
{
    return syscall(__NR_io_destroy, c);
}

static int
io_submit(aio_context_t c, long n, iocb** b)
{
    return syscall(__NR_io_submit, c, n, b);
}

static int
io_getevents(aio_context_t c, long min, long max, io_event* e, timespec* t)
{
    return syscall(__NR_io_getevents, c, min, max, e, t);
}

int main(int argc, char** argv)
{
    using namespace std::chrono;
    const auto n = 4096 * size_t(std::atoi(argv[1]));

    // Initialize the file descriptor. If O_DIRECT is not used, the kernel
    // will block on `io_submit` until the job finishes, because non-direct
    // IO via the `aio` interface is not implemented (to my knowledge).
    auto fd = ::open("dat/test.dat", O_RDONLY | O_DIRECT | O_NOATIME);
    if (fd < 0) {
        ::perror("Error opening file");
        return EXIT_FAILURE;
    }

    char* p;
    auto r = ::posix_memalign((void**)&p, 512, n);
    if (r != 0) {
        std::cerr << "posix_memalign failed." << std::endl;
        return EXIT_FAILURE;
    }
    auto del = [](char* p) { std::free(p); };
    std::unique_ptr<char[], decltype(del)> buf{p, del};

    // Initialize the IO context.
    aio_context_t c{0};
    r = io_setup(4, &c);
    if (r < 0) {
        ::perror("Error invoking io_setup");
        return EXIT_FAILURE;
    }

    // Setup I/O control block.
    iocb b;
    std::memset(&b, 0, sizeof(b));
    b.aio_fildes = fd;
    b.aio_lio_opcode = IOCB_CMD_PREAD;

    // Command-specific options for `pread`.
    b.aio_buf = (uint64_t)buf.get();
    b.aio_offset = 0;
    b.aio_nbytes = n;
    iocb* bs[1] = {&b};

    auto t1 = high_resolution_clock::now();
    auto r = io_submit(c, 1, bs);
    if (r != 1) {
        if (r == -1) {
            ::perror("Error invoking io_submit");
        }
        else {
            std::cerr << "Could not submit request." << std::endl;
        }
        return EXIT_FAILURE;
    }
    auto t2 = high_resolution_clock::now();
    auto count = duration_cast<duration<double>>(t2 - t1).count();
    // Print the wait time.
    std::cout << count << " ";

    io_event e[1];
    t1 = high_resolution_clock::now();
    r = io_getevents(c, 1, 1, e, NULL);
    t2 = high_resolution_clock::now();
    count = duration_cast<duration<double>>(t2 - t1).count();
    // Print the read time.
    std::cout << count << std::endl;

    r = io_destroy(c);
    if (r < 0) {
        ::perror("Error invoking io_destroy");
        return EXIT_FAILURE;
    }
}
4

3 に答える 3

5

私の理解では、AIO を完全にサポートする Linux 上のファイルシステムは (あるとしても) 非常に少ないということです。一部のファイルシステム操作は依然としてブロックされておりio_submit()、ファイルシステム操作を介して間接的に、そのようなブロック呼び出しを呼び出す場合があります。

私の理解では、カーネル AIO の主なユーザーは主に AIO が未加工のブロック デバイス (つまり、ファイル システムがない) で完全に非同期であることを気にかけています。基本的にデータベースベンダー。

これは、linux-aio メーリング リストからの関連する投稿です(スレッドの頭)

おそらく役立つ推奨事項:

/sys/block/xxx/queue/nr_requests 経由でリクエストを追加すると、問題は改善されます。

于 2014-01-08T00:43:56.297 に答える