我的笔记本电脑有一个 SSD 磁盘,它有 512 字节的物理磁盘扇区大小和 4,096 字节的逻辑磁盘扇区大小。我正在开发一个必须绕过所有操作系统缓存的 ACID 数据库系统,所以我直接从分配的内部内存 (RAM) 写入到 SSD 磁盘。我还在运行测试之前扩展了文件,并且在测试期间不调整它的大小。

现在这是我的问题,根据 SSD benchmarks 随机读写应该分别在 30 MB/s 到 90 MB/s 的范围内。但这是我的(相当可怕的)来自我无数性能测试的遥测数据:

  • 读取随机 512 字节块(物理扇区大小)时为 1.2 MB/s
  • 写入随机 512 字节块(物理扇区大小)时为 512 KB/s
  • 读取随机 4,096 字节块(逻辑扇区大小)时为 8.5 MB/s
  • 写入随机 4,096 字节块(逻辑扇区大小)时为 4.9 MB/s

  • 除了使用异步 I/O 之外,我还设置了 FILE_SHARE_READFILE_SHARE_WRITE 标志以禁用所有操作系统缓冲 - 因为我们的数据库是 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;
    }
    

    最佳答案

    随机 IO 性能无法以 MB/秒为单位很好地衡量。它以 IOPS 来衡量。 “读取随机 512 字节块时为 1.2 MB/s”=> 20000 IOPS。不错。将块大小加倍,您将获得 199% 的 MB/sec 和 99% 的 IOPS,因为读取 512 字节所需的时间与读取 1024 字节所需的时间几乎相同(几乎没有时间)。 SSD 并非没有像有时错误地假设的那样寻求成本。

    所以这些数字实际上一点也不差。

    SSD 受益于高队列深度。尝试一次发出多个 IO 并始终保持该数量未完成。最佳并发将在 1-32 的范围内。

    由于 SSD 具有硬件并发性,因此您可以期待单线程性能的一小部分。例如,我的 SSD 有 4 个并行“银行”。

    使用 FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING 是实现直接写入硬件所需的全部内容。如果这些标志不起作用,您的硬件不尊重这些标志,您将无能为力。所有服务器硬件都尊重这些标志,我还没有看到消费者磁盘不尊重这些标志。

    在这种情况下,共享标志没有意义。

    代码很好,虽然我不明白你为什么使用异步 IO,然后等待一个事件等待完成。这是没有意义的。要么使用同步 IO(其执行方式与异步 IO 大致相同),要么使用带有完成端口的异步 IO 且无需等待。

    关于c++ - 具有随机读/写的 SSD 原始 I/O 基准测试,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23469394/

    10-16 21:23