4

私のラップトップには、512 バイトの物理ディスク セクター サイズと 4,096 バイトの論理ディスク セクター サイズを持つ SSD ディスクがあります。すべての OS キャッシュをバイパスする必要がある ACID データベース システムで作業しているため、割り当てられた内部メモリ (RAM) から SSD ディスクに直接書き込みます。また、テストを実行する前にファイルを拡張し、テスト中にサイズを変更しません。

SSDベンチマークによると、ランダムな読み取りと書き込みはそれぞれ30 MB / sから90 MB / sの範囲内である必要があります。しかし、これは私の多数のパフォーマンス テストから得た (かなり恐ろしい) テレメトリです。

  • ランダムな 512 バイト ブロック (物理セクター サイズ) を読み取る場合は 1.2 MB/秒
  • ランダムな 512 バイト ブロック (物理セクター サイズ) を書き込む場合は 512 KB/秒
  • ランダムな 4,096 バイト ブロック (論理セクター サイズ) を読み取る場合は 8.5 MB/秒
  • ランダムな 4,096 バイト ブロック (論理セクター サイズ) を書き込む場合は 4.9 MB/秒

非同期 I/OI を使用するだけでなく、フラグFILE_SHARE_READFILE_SHARE_WRITEフラグを設定してすべての OS バッファリングを無効にします。データベースは ACID であるため、これを行う必要があります。これも試しFlushFileBuffers()ましたが、パフォーマンスがさらに低下しました。また、一部のコードで要求されるように、各非同期 I/O 操作が完了するのを待ちます。

これが私のコードです。問題があるのでしょうか、それとも I/O パフォーマンスが悪いのでしょうか?

HANDLE OpenFile(const wchar_t *fileName)
{
    // Set access method
    DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE ;

    // Set file flags
    DWORD fileFlags = FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING /*| FILE_FLAG_RANDOM_ACCESS*/;

    //File or device is being opened or created for asynchronous I/O
    fileFlags |= FILE_FLAG_OVERLAPPED ;

    // Exlusive use (no share mode)
    DWORD shareMode = 0;

    HANDLE hOutputFile = CreateFile(
        // File name
        fileName,
        // Requested access to the file 
        desiredAccess,
        // Share mode. 0 equals exclusive lock by the process
        shareMode,
        // Pointer to a security attribute structure
        NULL,
        // Action to take on file
        CREATE_NEW,
        // File attributes and flags
        fileFlags,
        // Template file
        NULL
    );
    if (hOutputFile == INVALID_HANDLE_VALUE)
    {
        int lastError = GetLastError();
        std::cerr << "Unable to create the file '" << fileName << "'. [CreateFile] error #" << lastError << "." << std::endl;
    }

    return hOutputFile;
}

DWORD ReadFromFile(HANDLE hFile, void *outData, _UINT64 bytesToRead, _UINT64 location, OVERLAPPED *overlappedPtr, 
    asyncIoCompletionRoutine_t completionRoutine)
{
    DWORD bytesRead = 0;

    if (overlappedPtr)
    {
        // Windows demand that you split the file byte locttion into high & low 32-bit addresses
        overlappedPtr->Offset = (DWORD)_UINT64LO(location);
        overlappedPtr->OffsetHigh = (DWORD)_UINT64HI(location);

        // Should we use a callback function or a manual event
        if (!completionRoutine && !overlappedPtr->hEvent)
        {
            // No manual event supplied, so create one. The caller must reset and close it themselves
            overlappedPtr->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
            if (!overlappedPtr->hEvent)
            {
                DWORD errNumber = GetLastError();
                std::wcerr << L"Could not create a new event. [CreateEvent] error #" << errNumber << L".";
            }
        }
    }

    BOOL result = completionRoutine ? 
        ReadFileEx(hFile, outData, (DWORD)(bytesToRead), overlappedPtr, completionRoutine) : 
        ReadFile(hFile, outData, (DWORD)(bytesToRead), &bytesRead, overlappedPtr);

    if (result == FALSE)
    {
        DWORD errorCode = GetLastError();
        if (errorCode != ERROR_IO_PENDING)
        {
            std::wcerr << L"Can't read sectors from file. [ReadFile] error #" << errorCode << L".";
        }
    }

    return bytesRead;
}
4

2 に答える 2

2

ランダム IO のパフォーマンスは、MB/秒では適切に測定されません。これは IOPS で測定されます。「ランダムな 512 バイト ブロックの読み取り時に 1.2 MB/秒」 => 20000 IOPS。悪くない。ブロックサイズを 2 倍にすると、1024 バイトを読み取るのとほぼ同じ時間 (ほとんど時間がかからない) で 512 バイトを読み取るのにかかるため、199% の MB/秒と 99% の IOPS が得られます。SSD は、時々誤って想定されているように、コストを求めないわけではありません。

したがって、数字は実際にはまったく悪くありません。

SSD は、高いキュー深度の恩恵を受けます。一度に複数の IO を発行してみて、その数を常に未処理のままにします。最適な同時実行数は、1 ~ 32 の範囲です。

SSD にはハードウェアの同時実行性があるため、シングル スレッドのパフォーマンスのわずかな倍数を期待できます。たとえば、私のSSDには4つの並列「バンク」があります。

FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERINGハードウェアへの直接書き込みを実現するために必要なのは、使用することだけです。これらのフラグが機能しない場合、ハードウェアはこれらのフラグを尊重せず、何もできません。すべてのサーバー ハードウェアはこれらのフラグを尊重しますが、そうでないコンシューマ ディスクは見たことがありません。

このコンテキストでは、共有フラグは意味がありません。

コードは問題ありませんが、非同期 IO を使用し、後でイベントを待機して完了を待機する理由がわかりません。それは意味がありません。同期 IO (非同期 IO とほぼ同じパフォーマンス) を使用するか、完了ポートを使用して待機せずに非同期 IO を使用します。

于 2014-05-05T11:16:02.410 に答える