Предыстория: я планирую использовать ZFS, и мне нужно найти правильный ashift
параметр для моего жесткого диска, который должен быть log2(sector_size)
, например 9 для секторов размером 512 байт.
Мой жесткий диск сообщает, что размер физического и логического сектора составляет 512 байт. Я читал, что некоторые жесткие диски сообщают неверную информацию, чтобы предотвратить проблемы совместимости с операционными системами, которые предполагают 512-байтовые сектора. Я не уверен, так ли это с моим жестким диском.
Поэтому я написал небольшую программу, которая поможет мне определить истинный размер физического сектора. Программа открывает пустой раздел на моем жестком диске и записывает блоки размером 4096 байт в 1000 случайно выбранных местоположений в пределах 1 ГиБ. Случайные местоположения сначала выравниваются по 4096 байтам, затем добавляется смещение. Программа выполняет эти 1000 случайных записей с использованием различных смещений и измеряет, сколько времени потребовалось для записи для каждого смещения. Первое смещение равно нулю, затем оно увеличивается с шагом 256 байт.
При открытии раздела на запись использую O_WRONLY | O_SYNC | O_DIRECT
flags, чтобы подобраться как можно ближе к оборудованию, то есть замкнуть как можно больше кешей. Я также убеждаюсь, что мой буфер правильно выровнен в памяти.
Вот чего я ожидал:
Но на самом деле я не могу заметить никакой разницы. На 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
Дополнительные наблюдения / примечания:
Кто-нибудь может это объяснить?
Для завершения вот моя программа (на случай, если кто-то захочет ее запустить, вставьте путь к пустому блочному устройству в 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;
}
Для ненулевых смещений адреса, на которые я пишу, не выравниваются по физическим секторам жесткого диска (независимо от того, имеет ли он 512 или 4096 байт физических секторов).
[...]
На тот случай, если сам мой раздел не выровнен по границе физического сектора, я также попытался увеличить смещение с шагом в 1 байт.
Какую ОС вы используете? Если это Linux, как вы могли выполнять запись с начальным смещением, которое не было кратным 512 байтам, когда вы использовали O_DIRECT
против базового блочного устройства?
Должна ли запись жесткого диска без выравнивания по секторам выполняться медленнее, чем запись с выравниванием по сектору?
Выравнивание по «истинному» размеру сектора должно быть менее проблемным, но насколько лучше зависит от устройства, данных и шаблона (Toshiba утверждает, что снижение производительности из-за несоосности может достигать 20%.). Твердотельные накопители (которые не являются тем, о чем вы спрашиваете, но, возможно, придется выполнить большие стирания перед сохранением данных) - отличный пример, потому что плохое выравнивание записи может привести к ненужному усилению записи. При этом мне сказали, что внутренние устройства современных устройств имеют секторы, намного превышающие 4 Кбайт, но почти никогда не подвергают их более высокому уровню.
Может ли кто-нибудь объяснить это [результаты, которые я вижу]?
Что ж, вы, скорее всего, увидите влияние чтения-изменения-записи (RMW), когда вы попадете в самую быструю возможную ситуацию, которая его поразит (поскольку разница будет наибольшей). Поскольку вы выполняете случайные записи, которые заставляют ОС ждать истинного завершения, вероятно, вы находитесь в более медленной ситуации, и снижение производительности просто теряется в шуме. Как утверждали другие, вам также необходимо победить любой кеш, который может маскировать проблемы - если вы каким-то образом заполнили кеш секторами, которые будут использоваться процессом RMW, то снова попадание может быть полностью замаскировано. Возможно, ваша программа-пример ошибочна. Вы рассматривали возможность использования фио?
Мой жесткий диск сообщает, что размер физического и логического сектора составляет 512 байт.
Если диск хочет лгать до такой степени (не указывая на лучший физический размер), попытка второго предположения о его поведении за пределами выравнивания разделов до 4 Кбайт будет сложной задачей. OpenZFS действительно содержит список дисков, размер фальшивого блока которых он попытается компенсировать. хотя.
Основная причина, по которой я читал для людей, использующих нестандартное изменение в ZFS, - это возможность добавлять в микс диски с исходным размером блока 4 Кбайт на более позднем этапе.
4096 байт x 1000 раз = 4 МБ данных. Скорее всего, ваш жесткий диск имеет 64 МБ кеш-памяти, если не больше, 256 МБ не редкость для современных дисков.
Ваша методика будет работать лучше, если вы значительно увеличите размер записи, возможно, в 64 раза, чтобы фактически увидеть характеристики физических дисков.