Reputation: 33579
How to programmatically determine in C or C++ whether a specified path is on a flash (SSD) drive or a magnetic hard drive? I'm only interested in fixed drives, not removable ones, although it will be nice to determine the type of a removable drive as well.
I'm looking for a solution that queries what Windows thinks of the specified path and the physical drive it's on. No benchmarking! Also note that looking for TRIM support is not a valid approach thanks to the SMR HDDs.
Upvotes: 8
Views: 2571
Reputation: 31599
From Old New Thing article: How can I tell whether a file is on an SSD?
This takes advantage of the trick we learned last time where you can make a storage query against a volume, and it will report the answer if the volume has a single extent.
We aren’t so much checking whether it’s on an SSD drive as we are checking whether seeks are free. That is true for SSDs, but it’s also true for RAM drives. But RAM drives are even faster than SSDs, so I think it’s okay to treat them as “super-awesome SSDs”...
This is a slightly modified version which doesn't need wil::unique_hfile
class in the original article.
This implementation does not throw if path is invalid, it will report non-SSD for invalid path or network path.
#include <iostream>
#include <Windows.h>
HANDLE GetVolumeHandleForFile(const wchar_t* filePath)
{
wchar_t volume_path[MAX_PATH];
if (!GetVolumePathName(filePath, volume_path, ARRAYSIZE(volume_path)))
return nullptr;
wchar_t volume_name[MAX_PATH];
if (!GetVolumeNameForVolumeMountPoint(volume_path,
volume_name, ARRAYSIZE(volume_name)))
return nullptr;
auto length = wcslen(volume_name);
if (length && volume_name[length - 1] == L'\\')
volume_name[length - 1] = L'\0';
return CreateFile(volume_name, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
}
bool IsFileOnSsd(const wchar_t* file_path)
{
bool is_ssd{ false };
HANDLE volume = GetVolumeHandleForFile(file_path);
if (volume == INVALID_HANDLE_VALUE)
{ return false; /*invalid path! throw?*/ }
STORAGE_PROPERTY_QUERY query{};
query.PropertyId = StorageDeviceSeekPenaltyProperty;
query.QueryType = PropertyStandardQuery;
DWORD count;
DEVICE_SEEK_PENALTY_DESCRIPTOR result{};
if (DeviceIoControl(volume, IOCTL_STORAGE_QUERY_PROPERTY,
&query, sizeof(query), &result, sizeof(result), &count, nullptr))
{ is_ssd = !result.IncursSeekPenalty; }
else { /*fails for network path, etc*/ }
CloseHandle(volume);
return is_ssd;
}
int main()
{
std::wcout << IsFileOnSsd(L"C:\\") << "\n";
std::wcout << IsFileOnSsd(L"D:\\") << "\n";
return 0;
}
Upvotes: 6
Reputation: 11910
Long comment:
You don't have to benchmark. You can have a dynamic load-balance between all N worker threads.
Take run-time of each task of each thread. Also take amount of work each had done. Then compute the normalized performance of each thread such that 0.5 means half of whole work in a batch of tasks.
After each completion of batch of tasks, do the same computation and adjust the work distribution ratios of threads accordingly with their normalized performances.
If its an SSD then all threads should converge to equal partition of tasks. If its an HDD then one of the threads should converge to 1.0 normalized performance and other threads to zero.
Since this is performance-aware, it should not make 10x slowdown on SSD when other applications(such as antivirus or another image processing app) use it at the same time (Assuming that the SSD becomes the HDD under heavy random-access usage from many apps) and be future-proof for any asymmetric cpu design with different performances on cores.
Upvotes: 0