背景:我计划使用 ZFS,我需要ashift
为我的硬盘找到正确的参数,例如log2(sector_size)
,512 字节扇区应该是 9。
我的硬盘报告物理和逻辑扇区大小为 512 字节。我读到有些硬盘报告错误信息是为了防止与假定扇区为 512 字节的操作系统发生兼容性问题。我不确定我的硬盘是否也是这种情况。
因此,我编写了一个小程序来帮助我确定真实的物理扇区大小。该程序在我的硬盘上打开一个空分区,并在 1 GiB 范围内随机选择的 1000 个位置写入 4096 字节的块。随机位置首先与 4096 字节对齐,然后添加偏移量。该程序使用不同的偏移量执行这 1000 次随机写入,并测量每个偏移量的写入时间。第一个偏移量为零,然后以 256 字节为步长增加。
当打开分区进行写入时,我会使用标志O_WRONLY | O_SYNC | O_DIRECT
尽可能接近硬件,即绕过尽可能多的缓存。我还确保我的缓冲区在内存中正确对齐。
以下是我期望的:
- 为了非零偏移,我写入的地址与硬盘的物理扇区不对齐(无论它有 512 字节还是 4096 字节的物理扇区)。至少有一个扇区只需部分修改,因此硬盘必须读更新该扇区,然后将其写回。这应该是慢点因为涉及读取(读取-修改-写入)。
- 为了零点偏移,无论硬盘驱动器有物理 512 字节扇区还是 4096 字节扇区,写入操作都不应要求读取任何扇区。所有受写入影响的扇区都应被覆盖。这应该是快点案件。
但实际上,我没注意到任何差异。1000 次写入总是需要大约 8.5 秒。偏移量似乎没有任何影响:
Offset Time (ms) for 1000 random writes
------ --------------------------------
0 8459.11
256 8450.69
512 8633.82
768 8533.94
1024 8467.36
1280 8450.63
1536 8525.72
1792 8533.96
2048 8450.64
2304 8450.79
2560 8442.37
2816 8442.38
3072 8442.28
3328 8450.82
3584 8442.27
3840 8450.81
补充观察/评论:
- 写入 512 字节的单元会产生类似的数字(即偏移量没有明显的影响)。
- 考虑到我的分区本身没有与物理扇区边界对齐,我还尝试以 1 字节为单位增加偏移量。这样,最终会找到“理想”偏移量 - 但我仍然无法识别任何差异。
有人能解释一下吗?
为了完整起见,这是我的程序(如果有人想运行它,请将空块设备的路径插入到调用中open
):
#include <chrono>
#include <fcntl.h>
#include <iostream>
#include <random>
#include <unistd.h>
int main()
{
const int bufferSize = 4096;
char buffer[bufferSize] __attribute__((aligned(4096)));
for (int offset = -256; offset < 4096; offset += 256)
{
std::mt19937 generator;
std::uniform_int_distribution<int> distribution(0, 1024 * 1024 * 1024 / 4096);
if (offset >= 0) std::cout << offset << "\t";
else std::cout << "Warming up ..." << std::endl;
int f = open("PATH_TO_EMPTY_BLOCK_DEVICE", O_WRONLY | O_SYNC | O_DIRECT);
auto t0 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000; ++i)
{
lseek(f, SEEK_SET, 4096 * distribution(generator) + offset);
if (write(f, buffer, bufferSize) != bufferSize) exit(1);
}
auto t1 = std::chrono::high_resolution_clock::now();
close(f);
if (offset >= 0) std::cout << (1000 * std::chrono::duration_cast<std::chrono::duration<double>>(t1 - t0).count()) << std::endl;
}
return 0;
}
答案1
4096 字节 x 1000 次 = 4 MB 数据。您的硬盘可能有 64 MB 的缓存,甚至更多,256 MB 在现代硬盘上并不罕见。
如果您大幅增加写入大小(可能为 64 倍),以便真正看到物理驱动器的特性,您的方法将会更好地发挥作用。
答案2
对于非零偏移量,我写入的地址与硬盘的物理扇区不对齐(无论它具有 512 还是 4096 字节的物理扇区)。
[...]
针对我的分区本身未与物理扇区边界对齐的情况,我还尝试以 1 字节为步长增加偏移量。
O_DIRECT
您使用的是哪种操作系统?如果是 Linux,那么当您针对底层块设备使用时,如何能够以不是 512 字节倍数的起始偏移量进行写入?
扇区未对齐的 HDD 写入是否应该比扇区对齐的写入慢?
与“真实”扇区大小对齐应该不会带来太大影响,但具体有多好则高度依赖于设备、数据和模式(东芝声称由于错位导致的性能下降可能高达 20%)。SSD(这不是您要问的,但在放下数据之前可能需要进行大量擦除)就是一个很好的例子,因为不良的写入对齐会导致不必要的写入放大。话虽如此,我听说现代设备内部的扇区远大于 4kbytes,但几乎从未将其暴露给更高级别。
有人能解释一下这个[我看到的结果]吗?
好吧,当您处于最快的情况下时,您最有可能看到读取-修改-写入 (RMW) 的影响(因为差异最大)。由于您正在执行随机写入,迫使操作系统等待真正的完成,因此您很可能处于较慢的情况,并且性能损失只是在噪音中丢失了。正如其他人所说,您还必须击败可能掩盖问题的任何缓存 - 如果您以某种方式用 RMW 过程将要使用的扇区填充了缓存,那么命中可能会再次被完全掩盖。可能是您的示例程序存在缺陷。您是否考虑过使用菲奥?
我的硬盘报告物理和逻辑扇区大小为 512 字节
如果磁盘想要达到这个程度(不表示更好的物理大小),那么尝试猜测它在将分区对齐到 4kbytes 之外的行为将会很有挑战性。OpenZFS 确实包含一个驱动器列表,它将尝试补偿这些驱动器的虚假块大小尽管。
据我所知,人们使用 ZFS 的非默认 ashift 的主要原因是为了能够在稍后将具有 4kbyte 本机块大小的磁盘添加到组合中。