Поэтому я не буду утомлять вас, почему, но мое приложение может по желанию выполнить некоторую проверку целостности очень больших файлов (до 50 ГБ) с помощью CRC. Поскольку я не хочу убивать пользовательские машины, если они включают эту опцию, я установил подсказку IoPriorityHintVeryLow на дескриптор, а также устанавливал приоритет потока в THREAD_MODE_BACKGROUND_BEGIN, используя этот API.
Отнимающая много времени часть моего кода выглядит так:
//
// Read one block of the changed data at a time, checking each CRC
//
DWORD blockNum = 0;
vector<BYTE> changeBuffer(DIRTY_BLOCK_SIZE);
outputDirtyBlockMap.reserve(crcList.size() / 8);
while (::ReadFile(hChangedFile, changeBuffer.data(), DIRTY_BLOCK_SIZE, &bytesRead, NULL) && bytesRead > 0)
{
// Check for cancellation every 500 blocks, doing it every time reduces CPU performance by 50% since WaitForSingleObject is quite expensive
if ((blockNum % 500 == 0) && IsCancelEventSignalled(hCancel))
{
RETURN_TRACED(ERROR_CANCELLED);
}
// Increase the size of the dirty block map?
ULONG mapByte = blockNum / 8;
if (mapByte == outputDirtyBlockMap.size())
{
outputDirtyBlockMap.resize(mapByte + 1);
}
DWORD mapBitNum = blockNum & 0x7L;
UCHAR mapBit = 1 << (7 - mapBitNum);
if (driverDirtyBlockMap.size() > mapByte && (driverDirtyBlockMap[mapByte] & mapBit))
{
//
// The bit is already set in the drivers block map, we don't have to bother generating comparing CRCs for this block
//
outputDirtyBlockMap[mapByte] |= mapBit;
}
else
{
// Validate that the CRC hasn't changed, if it has, mark it as such in the dirty block map
DWORD newCrc = CRC::Crc32(changeBuffer.data(), changeBuffer.size());
if ((blockNum >= crcList.size() || newCrc != crcList[blockNum]))
{
OPTIONAL_DEBUG(DEBUG_DIRTY_BLOCK_MAP & DEBUG_VERBOSE, "Detected change at block [%u], CRC [new 0x%x != old 0x%x]", blockNum, newCrc, blockNum < crcList.size() ? crcList[blockNum] : 0x0);
// The CRC is changed or the file has grown, mark it as such in the dirty block map
outputDirtyBlockMap[mapByte] |= mapBit;
}
}
++blockNum;
}
Когда я профилировал этот код, я был очень удивлен, обнаружив, что, когда этот цикл выполняется в THREAD_MODE_BACKGROUND_BEGIN, он занимает 74 секунды, чтобы запустить файл размером 500 МБ. При работе с THREAD_PRIORITY_LOWEST требуется более 2,7 секунды для запуска файла размером 500 МБ. (Я проверял это около 8 раз сейчас, и это было в среднем)
В обоих случаях машина, на которой я тестировал, была простаивала, кроме запуска этого цикла. Итак, вопрос:
Почему THREAD_MODE_BACKGROUND_BEGIN делает это так долго? Я бы подумал, что если машина больше ничего не делает, она все равно должна работать так же быстро, как и с любым другим приоритетом, потому что ей не нужно расставлять приоритеты?
Есть ли что-то, что я должен знать об этом приоритете, который я не смог выяснить из документов?
То, что THREAD_MODE_ * отличается от THREAD_PRIORITY_ *, не удивительно ли?
Я не знаю, документированы ли где-нибудь точные различия, но меня не удивит, если фоновый режим попытается запустить все на одном ядре, если процессор поддерживает парковку ядра и на более низкой частоте.
Документация SetThreadPriority также указывает на некоторые изменения в любом вводе / выводе, который выполняет поток:
Значения THREAD_PRIORITY_ * влияют на приоритет планирования ЦП потока. Для потоков, которые выполняют фоновую работу, такую как файловый ввод-вывод, сетевой ввод-вывод или обработка данных, недостаточно отрегулировать приоритет планирования ЦП; даже незанятый приоритетный поток ЦП может легко влиять на быстродействие системы, когда он использует диск и память. Потоки, которые выполняют фоновую работу, должны использовать значения THREAD_MODE_BACKGROUND_BEGIN и THREAD_MODE_BACKGROUND_END, чтобы отрегулировать свои приоритеты планирования ресурсов; потоки, которые взаимодействуют с пользователем, не должны использовать THREAD_MODE_BACKGROUND_BEGIN.
Вы пытались измерить, чтобы увидеть, если потеря производительности в ReadFile
или расчет CRC?
Настройка фонового режима имеет следующие эффекты:
При установке относительного приоритета потока на LOWEST имеет следующий эффект:
Таким образом, в целом, особенно если вы ограничены вводом / выводом (но даже в случае привязки к процессору), вы определенно ожидаете поток с приоритетом 4, работающий с очень низким приоритетом ввода / вывода и фоновой памятью (1) выполнить намного хуже, чем поток с приоритетом памяти переднего плана (5) + нормальный приоритет ввода-вывода с приоритетом 6 …