2014-04-29 31 views
11

我想根據系統驅動器是否爲SSD來更改我的C++應用程序的性能和行爲。 例子:在Windows中檢測SSD

  • 隨着SSD,我想我的遊戲服務器的應用程序完全加載每個地圖,與所有對象,以最大限度地提高性能。
  • 使用HDD時,我希望我的遊戲服務器應用程序僅加載每個地圖中的基本對象和實體,而不加載外部對象。

我見過http://msdn.microsoft.com/en-gb/library/windows/desktop/aa364939(v=vs.85).aspx,這是確定某一驅動器是硬盤,CD-ROM,DVD-ROM,可移動介質等的方式,但它仍然無法檢測到主系統驅動器是否是一個SSD。 我也見過Is there any way of detecting if a drive is a SSD?,但該解決方案僅適用於Linux。

我以爲我可以以某種方式生成一個大的罰款(500MB),然後時間多長時間來寫文件,但其他系統變量可以很容易地影響結果。

在Windows中,使用C++,有什麼方法可以獲取主系統驅動器是否是SSD?

+2

這聽起來像一個複雜的問題,可以由用戶控制的開關更容易處理。 – kusma

+7

我會去測量時間,因爲重要的是速度,而不是它本身就是SSD。 – jcoder

+0

看一看[this](http://www.msfn.org/board/topic/151836-need-script-to-detect-ssd/) – Theolodis

回答

13

我相信你使用的是錯誤的工具。而不是基於SSD作爲驅動器的假設,你應該使你的代碼適用於緩慢和快速的驅動器,例如先加載基本對象,然後再加載。在三年內,發明可能會使固定硬盤速度超過SSD,這會破壞您的代碼。 單純以速度爲基礎的方法也適用於RAM光盤,NFS,USB3.0棒以及其他您沒有或無法做到的事情。

編輯:一個硬盤實際上不是一個慢速SSD。雖然他們都快速閱讀和寫一個硬盤驅動器需要大量的時間尋求。因此,使用兩種不同的訪問策略是有意義的:通過SSD的隨機訪問選擇重要數據並順序讀取HDD。你可能會逃避只執行順序策略,因爲它應該仍然適用於SSD。儘管檢查硬盤而不是固態硬盤更有意義,因爲您需要對硬盤進行特殊處理,而SSD,RAMdisc,NFS等不應受到查找時間的限制,因此可以對其進行相同處理。

+0

我接受了另一個答案,因爲它提供了一個解決方案,但是這個答案是有用的,不應該被解僱。 – cybermonkey

4

不用擔心驅動器類型。通過閱讀一些加載的遊戲數據來進行測量,並決定使用哪種策略。 (不要忘記做一個配置選項:)

下面我的直覺告訴我,這種做法是錯誤的。如果有人有一個慢磁盤,然後預加載應該更重要,因爲在飛行加載會導致口吃。另一方面,如果驅動器足夠快,我不需要浪費內存,因爲我可以快速加載數據。

6

是的,確定驅動器是否爲SSD的可能性很高。 SSD通常支持TRIM命令,因此我會檢查驅動器是否支持TRIM命令。

在Windows中,您可以使用IOCTL_STORAGE_QUERY_PROPERTY獲取DEVICE_TRIM_DESCRIPTOR結構,該結構會告訴您TRIM是否已啓用。

如果你真的知道你在做什麼,你可以得到原始的IDENTIFY DEVICE包,並自己解釋數據。對於SATA驅動器,它將是字169位0。

+0

非常好。它實際上適用於我所有的SSD。現在如何檢測驅動器的RPM,哪個SSD應報告爲0或不報告。有沒有人有這樣的工作代碼?很明顯'IOCTL_SCSI_MINIPORT'可以用於這個。目前我能找到的最好的答案是這個半答案的問題:https://social.msdn.microsoft.com/Forums/en-US/66a9f916-bbb6-4e51-91f5-8587968691cb/get-rpm-of-disk? forum = vcmfcatl – c00000fd

+0

RPM在IDENTIFY DEVICE包的NOMINAL MEDIA ROTATION RATE字段(字217)中報告。 –

+0

@tchau。dev是什麼意思DEVICE_TRIM_DESCRIPTOR-> TrimEnabled總是等於word169-> bit0? (因此,如果設備報告「1」,它只意味着它支持TRIM,並不意味着它實際上不起作用,例如像[這裏]所述(http://askubuntu.com/a/439655/) 92948)。) – Igor

10

已經做了一些研究,並利用此頁面上的答案信息,這裏是用C WinAPIs我的實現對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; 
#endif 


struct ATAIdentifyDeviceQuery 
{ 
    ATA_PASS_THROUGH_EX header; 
    WORD data[256]; 
}; 

最後,我的測試結論。

我有幾個三星SSD通過SATA電纜連接,一個PCIe SSD驅動器直接連接到使用PCIe插槽的邏輯板。我還有一個大型的內置Western Digital HDD(旋轉驅動器),它也通過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。我並不是說它也適用於你的情況。這只是我發現我擁有的驅動器。