我的笔记本电脑有一个 SSD 磁盘,其物理磁盘扇区大小为 512 字节,逻辑磁盘扇区大小为 4,096 字节。我正在开发一个必须绕过所有操作系统缓存的 ACID 数据库系统,因此我直接从分配的内部内存 (RAM) 写入 SSD 磁盘。我还在运行测试之前扩展文件,并且在测试期间不调整文件大小。
现在这是我的问题,根据 SSD benchmarks随机读取和写入应分别在 30 MB/s 到 90 MB/s 的范围内。但这是我从无数性能测试中获得的(相当可怕的)遥测数据:
- 读取随机 512 字节 block (物理扇区大小)时为 1.2 MB/s
- 写入随机 512 字节 block (物理扇区大小)时为 512 KB/s
- 读取随机 4,096 字节 block (逻辑扇区大小)时为 8.5 MB/s
- 写入随机 4,096 字节 block (逻辑扇区大小)时为 4.9 MB/s
除了使用异步 I/O,我还设置了 FILE_SHARE_READ
和 FILE_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 字节 block 时为 1.2 MB/s”=> 20000 IOPS。不错。 block 大小加倍,您将获得 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/