Обнаружение SSD в Windows

Я хотел бы изменить производительность и поведение моего приложения C ++ в зависимости от того, является ли системный диск SSD или нет.
Пример:

  • В 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 или нет?

11

Решение

Вы можете использовать Microsoft WMI Class MSFT_PhysicalDisk. Медиатип 4 — это SSD, а SpindleSpeed ​​будет 0. Для более подробной информации смотрите ссылку.

https://msdn.microsoft.com/en-us/library/windows/desktop/hh830532(v=vs.85)

12

Другие решения

Я считаю, что вы используете не тот инструмент. Вместо того, чтобы делать предположения на основе того, что диск является SSD, вы должны заставить свой код хорошо работать с медленными и быстрыми дисками, например, сначала загружая необходимые объекты, а затем — остальные. Через три года изобретение […] может сделать обычные жесткие диски быстрее, чем твердотельные накопители, что нарушит ваш код.
Работа исключительно на основе скорости также будет работать с RAM-дисками, NFS, USB3.0-флешками и другими вещами, о которых вы не мечтали или не можете ничего сказать.

РЕДАКТИРОВАТЬ: HDD на самом деле не то же самое, что медленный SSD. Хотя они оба быстро читают и пишут, жесткий диск требует значительного времени для поиска. Таким образом, имеет смысл использовать две разные стратегии доступа: выбор важных данных с помощью произвольного доступа для SSD и последовательное чтение для HDD. Возможно, вам не удастся реализовать только последовательную стратегию, так как она все равно будет работать нормально с твердотельными накопителями. Тем не менее, имеет смысл проверять наличие жесткого диска вместо SSD, поскольку необходимо обращаться с жестким диском особенным, в то время как SSD, RAMdisc, NFS и т. Д. Не должны страдать от времени поиска и поэтому могут обрабатываться одинаково.

14

Проведя некоторые исследования и используя информацию из ответов на этой странице, вот моя реализация с использованием 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. Я не говорю, что это будет и в вашем случае. Это просто моя находка с накопителями, которыми я владею.

13

Да, есть большой шанс определить, является ли диск SSD. SSD обычно поддерживают команду TRIM, поэтому я хотел бы проверить, поддерживает ли привод команду TRIM.

В Windows вы можете использовать IOCTL_STORAGE_QUERY_PROPERTY чтобы получить DEVICE_TRIM_DESCRIPTOR структура, которая скажет вам, если включен TRIM.

Если вы действительно знаете, что делаете, вы можете получить необработанный пакет IDENTIFY DEVICE и интерпретировать данные самостоятельно. Для дисков SATA это будет слово 169 бит 0.

6

не беспокойтесь о типе привода. сделайте измерение, прочитав некоторые из ваших игровых данных, которые загружаются в любом случае, и решите, какую стратегию использовать. (не забудьте сделать опцию конфигурации 🙂

Тем не менее, мой внутренний инстинкт подсказывает мне, что подход неверен. если у кого-то медленный диск, то предварительная загрузка должна быть более важной, поскольку загрузка на лету вызовет заикание. С другой стороны, если диск достаточно быстрый, мне не нужно тратить память, потому что я могу загружать данные на лету достаточно быстро.

4
По вопросам рекламы [email protected]