Я хотел бы изменить производительность и поведение моего приложения C ++ в зависимости от того, является ли системный диск SSD или нет.
Пример:
я видел http://msdn.microsoft.com/en-gb/library/windows/desktop/aa364939(v=vs.85).aspx, это способ определения того, является ли определенный привод жестким диском, CD-ROM, DVD-ROM, съемным носителем и т. д., но он по-прежнему не может определить, является ли основной системный диск SSD.
Я также видел Есть ли способ определить, является ли накопитель SSD?, но решение относится только к Linux.
Я подумал, что мог бы как-то сгенерировать большой штраф (500 МБ), а затем время, необходимое для записи файла, но, тем не менее, другие системные переменные могут легко повлиять на результат.
В Windows, используя C ++, есть ли способ узнать, является ли основной системный диск SSD или нет?
Вы можете использовать Microsoft WMI Class MSFT_PhysicalDisk. Медиатип 4 — это SSD, а SpindleSpeed будет 0. Для более подробной информации смотрите ссылку.
https://msdn.microsoft.com/en-us/library/windows/desktop/hh830532(v=vs.85)
Я считаю, что вы используете не тот инструмент. Вместо того, чтобы делать предположения на основе того, что диск является SSD, вы должны заставить свой код хорошо работать с медленными и быстрыми дисками, например, сначала загружая необходимые объекты, а затем — остальные. Через три года изобретение […] может сделать обычные жесткие диски быстрее, чем твердотельные накопители, что нарушит ваш код.
Работа исключительно на основе скорости также будет работать с RAM-дисками, NFS, USB3.0-флешками и другими вещами, о которых вы не мечтали или не можете ничего сказать.
РЕДАКТИРОВАТЬ: HDD на самом деле не то же самое, что медленный SSD. Хотя они оба быстро читают и пишут, жесткий диск требует значительного времени для поиска. Таким образом, имеет смысл использовать две разные стратегии доступа: выбор важных данных с помощью произвольного доступа для SSD и последовательное чтение для HDD. Возможно, вам не удастся реализовать только последовательную стратегию, так как она все равно будет работать нормально с твердотельными накопителями. Тем не менее, имеет смысл проверять наличие жесткого диска вместо SSD, поскольку необходимо обращаться с жестким диском особенным, в то время как SSD, RAMdisc, NFS и т. Д. Не должны страдать от времени поиска и поэтому могут обрабатываться одинаково.
Проведя некоторые исследования и используя информацию из ответов на этой странице, вот моя реализация с использованием C WinAPI для Windows 7 и более поздних версий:
//Open drive as such: "\\?\PhysicalDriveX" where X is the drive number
//INFO: To get drive number from a logical drive letter, check this method:
// (But keep in mind that a single logical drive, or a volume,
// can span across several physical drives, as a "spanned volume.")
// http://stackoverflow.com/a/11683906/843732
#include <WinIoCtl.h>
#include <Ntddscsi.h>
DWORD bytesReturned;
//As an example, let's test 1st physical drive
HANDLE hDevice = ::CreateFile(L"\\\\?\\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE, //We need write access to send ATA command to read RPMs
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if(hDevice != INVALID_HANDLE_VALUE)
{
//Check TRIM -- should be Y for SSD
_tprintf(L"TRIM=");
STORAGE_PROPERTY_QUERY spqTrim;
spqTrim.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceTrimProperty;
spqTrim.QueryType = PropertyStandardQuery;
bytesReturned = 0;
DEVICE_TRIM_DESCRIPTOR dtd = {0};
if(::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&spqTrim, sizeof(spqTrim), &dtd, sizeof(dtd), &bytesReturned, NULL) &&
bytesReturned == sizeof(dtd))
{
//Got it
_tprintf(L"%s", dtd.TrimEnabled ? L"Y" : L"N");
}
else
{
//Failed
int err = ::GetLastError();
_tprintf(L"?");
}//Check the seek-penalty value -- should be N for SSD
_tprintf(L", seekPenalty=");
STORAGE_PROPERTY_QUERY spqSeekP;
spqSeekP.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceSeekPenaltyProperty;
spqSeekP.QueryType = PropertyStandardQuery;
bytesReturned = 0;
DEVICE_SEEK_PENALTY_DESCRIPTOR dspd = {0};
if(::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&spqSeekP, sizeof(spqSeekP), &dspd, sizeof(dspd), &bytesReturned, NULL) &&
bytesReturned == sizeof(dspd))
{
//Got it
_tprintf(L"%s", dspd.IncursSeekPenalty ? L"Y" : L"N");
}
else
{
//Failed
int err = ::GetLastError();
_tprintf(L"?");
}//Get drive's RPMs reading -- should be 1 for SSD
//CODE SOURCE: https://emoacht.wordpress.com/2012/11/06/csharp-ssd/
_tprintf(L", RPM=");
ATAIdentifyDeviceQuery id_query;
memset(&id_query, 0, sizeof(id_query));
id_query.header.Length = sizeof(id_query.header);
id_query.header.AtaFlags = ATA_FLAGS_DATA_IN;
id_query.header.DataTransferLength = sizeof(id_query.data);
id_query.header.TimeOutValue = 5; //Timeout in seconds
id_query.header.DataBufferOffset = offsetof(ATAIdentifyDeviceQuery, data[0]);
id_query.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE
bytesReturned = 0;
if(::DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH,
&id_query, sizeof(id_query), &id_query, sizeof(id_query), &bytesReturned, NULL) &&
bytesReturned == sizeof(id_query))
{
//Got it
//Index of nominal media rotation rate
//SOURCE: http://www.t13.org/documents/UploadedDocuments/docs2009/d2015r1a-ATAATAPI_Command_Set_-_2_ACS-2.pdf
// 7.18.7.81 Word 217
//QUOTE: Word 217 indicates the nominal media rotation rate of the device and is defined in table:
// Value Description
// --------------------------------
// 0000h Rate not reported
// 0001h Non-rotating media (e.g., solid state device)
// 0002h-0400h Reserved
// 0401h-FFFEh Nominal media rotation rate in rotations per minute (rpm)
// (e.g., 7 200 rpm = 1C20h)
// FFFFh Reserved
#define kNominalMediaRotRateWordIndex 217
_tprintf(L"%d", (UINT)id_query.data[kNominalMediaRotRateWordIndex]);
}
else
{
//Failed
int err = ::GetLastError();
_tprintf(L"?");
}_tprintf(L"\n");
::CloseHandle(hDevice);
}
Если у вас нет драйвера DDK, вот несколько определений:
#ifndef StorageDeviceTrimProperty
#define StorageDeviceTrimProperty 8
#endif
#ifndef DEVICE_TRIM_DESCRIPTOR
typedef struct _DEVICE_TRIM_DESCRIPTOR {
DWORD Version;
DWORD Size;
BOOLEAN TrimEnabled;
} DEVICE_TRIM_DESCRIPTOR, *PDEVICE_TRIM_DESCRIPTOR;
#endif#ifndef StorageDeviceSeekPenaltyProperty
#define StorageDeviceSeekPenaltyProperty 7
#endif
#ifndef DEVICE_SEEK_PENALTY_DESCRIPTOR
typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR {
DWORD Version;
DWORD Size;
BOOLEAN IncursSeekPenalty;
} DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR;
#endifstruct ATAIdentifyDeviceQuery
{
ATA_PASS_THROUGH_EX header;
WORD data[256];
};
Наконец, заключение моих тестов.
У меня есть несколько SSD-накопителей Samsung, подключенных через кабель SATA, и один SSD-накопитель PCIe, подключенный напрямую к логической плате через разъем PCIe. У меня также есть один большой внутренний жесткий диск Western Digital (вращающийся диск), который также подключен через кабель SATA, и несколько внешних вращающихся жестких дисков.
Вот что я получаю за них:
Samsung SSD 256GB: TRIM=Y, seekPenalty=N, RPM=1
Samsung SSD 500GB: TRIM=Y, seekPenalty=N, RPM=1
PCIs SSD: TRIM=Y, seekPenalty=?, RPM=0
Internal WD HDD: TRIM=N, seekPenalty=?, RPM=0
External WD HDD: TRIM=?, seekPenalty=?, RPM=?
External Cavalry HDD: TRIM=?, seekPenalty=Y, RPM=?
Итак, как вы видите, в моем случае, единственный параметр, который является правильным для всех 6 дисков — это TRIM. Я не говорю, что это будет и в вашем случае. Это просто моя находка с накопителями, которыми я владею.
Да, есть большой шанс определить, является ли диск SSD. SSD обычно поддерживают команду TRIM, поэтому я хотел бы проверить, поддерживает ли привод команду TRIM.
В Windows вы можете использовать IOCTL_STORAGE_QUERY_PROPERTY чтобы получить DEVICE_TRIM_DESCRIPTOR структура, которая скажет вам, если включен TRIM.
Если вы действительно знаете, что делаете, вы можете получить необработанный пакет IDENTIFY DEVICE и интерпретировать данные самостоятельно. Для дисков SATA это будет слово 169 бит 0.
не беспокойтесь о типе привода. сделайте измерение, прочитав некоторые из ваших игровых данных, которые загружаются в любом случае, и решите, какую стратегию использовать. (не забудьте сделать опцию конфигурации 🙂
Тем не менее, мой внутренний инстинкт подсказывает мне, что подход неверен. если у кого-то медленный диск, то предварительная загрузка должна быть более важной, поскольку загрузка на лету вызовет заикание. С другой стороны, если диск достаточно быстрый, мне не нужно тратить память, потому что я могу загружать данные на лету достаточно быстро.