Непонятное время, затраченное на использование файла с отображением памяти
Я пишу процедуру для сравнения двух файлов с использованием файла с отображением памяти. В случае, если файлы слишком большие, чтобы их можно было сопоставить за один раз. Я разделял файлы и составлял их по частям. Например, чтобы отобразить файл 1049 МБ, я разделил его на 512 МБ + 512 МБ + 25 МБ.
Все прекрасно работает, кроме одного: для сравнения остатка всегда требуется намного больше времени (25 МБ в этом примере), хотя логика кода точно такая же. 3 наблюдения:
- Не имеет значения, что сначала сравнивается: первая часть (512 МБ * N) или остаток (25 МБ в этом примере) на первом месте, результат остается тем же самым
- дополнительное время в остатке, по-видимому, расходуется в режиме пользователя
- Профилирование в VS2010 beta 1 показывает, что время тратится на t
std::_Equal()
, но эта функция в основном (профайлер говорит 100%) ждет ввода-вывода и других потоков.
Я пробовал
- изменение VIEW_SIZE_FACTOR на другое значение
- заменив лямбда-функтор на функцию-член
- изменение размера файла в тесте
- изменение порядка выполнения остатка до/после цикла
Результат был довольно последовательным: он занимает намного больше времени в оставшейся части и в режиме пользователя.
Я подозреваю, что он имеет какое-то отношение к тому факту, что сопоставленный размер не кратен выравниванию сопоставления (64 КБ в моей системе), но не уверен, как это сделать.
Ниже приведен полный код для процедуры и время, измеренное для файла 3G.
Может кто-нибудь объяснить это, спасибо?
// using memory-mapped file
template <size_t VIEW_SIZE_FACTOR>
struct is_equal_by_mmapT
{
public:
bool operator()(const path_type& p1, const path_type& p2)
{
using boost::filesystem::exists;
using boost::filesystem::file_size;
try
{
if(!(exists(p1) && exists(p2))) return false;
const size_t segment_size = mapped_file_source::alignment() * VIEW_SIZE_FACTOR;
// lanmbda
boost::function<bool(size_t, size_t)> segment_compare =
[&](size_t seg_size, size_t offset)->bool
{
using boost::iostreams::mapped_file_source;
boost::chrono::run_timer t;
mapped_file_source mf1, mf2;
mf1.open(p1, seg_size, offset);
mf2.open(p2, seg_size, offset);
if(! (mf1.is_open() && mf2.is_open())) return false;
if(!equal (mf1.begin(), mf1.end(), mf2.begin())) return false;
return true;
};
boost::uintmax_t size = file_size(p1);
size_t round = size / segment_size;
size_t remainder = size & ( segment_size - 1 );
// compare the remainder
if(remainder > 0)
{
cout << "segment size = "
<< remainder
<< " bytes for the remaining round";
if(!segment_compare(remainder, segment_size * round)) return false;
}
//compare the main part. take much less time, even
for(size_t i = 0; i < round; ++i)
{
cout << "segment size = "
<< segment_size
<< " bytes, round #" << i;
if(!segment_compare(segment_size, segment_size * i)) return false;
}
}
catch(std::exception& e)
{
cout << e.what();
return false;
}
return true;
}
};
typedef is_equal_by_mmapT<(8<<10)> is_equal_by_mmap; // 512MB
выход:
размер сегмента = 354410496 байт для оставшегося раунда
real 116.892s, cpu 56.201s (48.1%), пользователь 54.548s, система 1.652s
размер сегмента = 536870912 байтов, раунд # 0
real 72.258s, cpu 2.273s (3.1%), пользователь 0.320s, система 1.953s
размер сегмента = 536870912 байтов, раунд № 1
real 75.304s, cpu 1.943s (2.6%), пользователь 0.240s, система 1.702s
размер сегмента = 536870912 байтов, раунд № 2
real 84.328s, cpu 1.783s (2.1%), пользователь 0.320s, система 1.462s
размер сегмента = 536870912 байтов, раунд № 3
real 73.901s, cpu 1.702s (2.3%), пользователь 0.330s, система 1.372s
Дополнительные наблюдения после предложений респондентов
Далее разделим остаток на тело и хвост (остаток = тело + хвост), где
- body = N * alignment() и tail < 1 * alignment()
- body = m * alignment() и tail < 1 * alignment() + n * alignment(), где m четное.
- body = m * alignment() и tail < 1 * alignment() + n * alignment(), где m - показатель степени 2.
- body = N * alignment(), а tail = остаток - тело. N является случайным.
общее время остается неизменным, но я вижу, что время не обязательно относится к хвосту, но к размеру тела и хвоста. большая часть занимает больше времени. Время - ПОЛЬЗОВАТЕЛЬСКОЕ ВРЕМЯ, что для меня непостижимо.
Я также просматриваю ошибки страниц через Procexp.exe. остаток НЕ принимает больше ошибок, чем основной цикл.
Обновления 2
Я выполнил некоторые тесты на других рабочих станциях, и, похоже, проблема связана с конфигурацией оборудования.
Тестовый код
// compare the remainder, alternative way
if(remainder > 0)
{
//boost::chrono::run_timer t;
cout << "Remainder size = "
<< remainder
<< " bytes \n";
size_t tail = (alignment_size - 1) & remainder;
size_t body = remainder - tail;
{
boost::chrono::run_timer t;
cout << "Remainder_tail size = " << tail << " bytes";
if(!segment_compare(tail, segment_size * round + body)) return false;
}
{
boost::chrono::run_timer t;
cout << "Remainder_body size = " << body << " bytes";
if(!segment_compare(body, segment_size * round)) return false;
}
}
Замечание:
На других 2 ПК с теми же конфигурациями h/w с моим, результат последователен, как показано ниже:
------ VS2010Beta1ENU_VSTS.iso [1319909376 bytes] ------
Размер остатка = 44840960 байт
Размер Remainder_tail = 14336 байт
real 0.060s, cpu 0.040s (66.7%), пользователь 0.000s, система 0.040s
Размер Remainder_body = 44826624 байта
real 13.601s, cpu 7.731s (56.8%), пользователь 7.481s, система 0.250s
размер сегмента = 67108864 байт, общий раунд # = 19
real 172.476s, cpu 4.356s (2.5%), пользователь 0.731s, система 3.625s
Однако запуск одного и того же кода на ПК с другой конфигурацией h/w дало:
------ VS2010Beta1ENU_VSTS.iso [1319909376 bytes] ------
Размер остатка = 44840960 байт
Размер Remainder_tail = 14336 байт
real 0.013s, cpu 0.000s (0.0%), пользователь 0.000s, система 0.000s
Размер Remainder_body = 44826624 байта
реальный 2.468s, cpu 0.188s (7.6%), пользователь 0.047s, система 0.141s
размер сегмента = 67108864 байт, общий раунд # = 19
real 65.587s, ccu 4.578s (7.0%), пользователь 0.844s, система 3.734s
Информация о системе
Моя рабочая станция дает непонятные сроки:
Название ОС: Microsoft Windows XP Professional
Версия ОС: 5.1.2600 Service Pack 3 Build 2600
ОС Производитель: Microsoft Corporation
Конфигурация ОС: рабочая станция участника
OS Тип сборки: Uniprocessor Free
Оригинальная дата установки: 2004-01-27, 23:08
Время работы системы: 3 дня, 2 часа, 15 минут, 46 секунд
Производитель системы: Dell Inc.
Модель системы: OptiPlex GX520
Тип системы: ПК на базе X86
Процессор (ы): 1 Установленный процессор (ы).
[01]: x86 Family 15 Model 4 Stepping 3 GenuineIntel ~2992 Mhz
Версия BIOS: DELL - 7
Каталог Windows: C:\WINDOWS
Системный каталог: C:\WINDOWS\system32
Загрузочное устройство:\Device\HarddiskVolume2
Системный язык: zh-cn; китайский (Китай)
Входной язык: zh-cn; китайский (Китай)
Часовой пояс: (GMT + 08: 00) Пекин, Чунцин, Гонконг, Урумчи
Общая физическая память: 3,574 МБ
Доступная физическая память: 1,986 МБ
Виртуальная память: Макс. размер: 2,048 МБ
Виртуальная память: доступно: 1,916 МБ
Виртуальная память: при использовании: 132 МБ
Местоположение файла: C:\pagefile.sys
Карта (и) NetWork: Установлены 3 сетевых адаптера.
[01]: VMware Virtual Ethernet Adapter for VMnet1
Connection Name: VMware Network Adapter VMnet1
DHCP Enabled: No
IP address(es)
[01]: 192.168.75.1
[02]: VMware Virtual Ethernet Adapter for VMnet8
Connection Name: VMware Network Adapter VMnet8
DHCP Enabled: No
IP address(es)
[01]: 192.168.230.1
[03]: Broadcom NetXtreme Gigabit Ethernet
Connection Name: Local Area Connection 4
DHCP Enabled: Yes
DHCP Server: 10.8.0.31
IP address(es)
[01]: 10.8.8.154
Другая рабочая станция, дающая "правильное" время:
Название ОС: Microsoft Windows XP Professional
Версия ОС: 5.1.2600 Service Pack 3 Build 2600
ОС Производитель: Microsoft Corporation
Конфигурация ОС: рабочая станция участника
Тип сборки ОС: безпроцессорный
Оригинальная дата установки: 18.05.2009, 2:28:18
Время работы системы: 21 день, 5 часов, 0 минут, 49 секунд
Производитель системы: Dell Inc.
Модель системы: OptiPlex 755
Тип системы: ПК на базе X86
Процессор (ы): 1 Установленный процессор (ы).
[01]: x86 Family 6 Model 15 Stepping 13 GenuineIntel ~2194 Mhz
Версия BIOS: DELL - 15
Каталог Windows: C:\WINDOWS
Системный каталог: C:\WINDOWS\system32
Загрузочное устройство:\Device\HarddiskVolume1
Системный язык: zh-cn; китайский (Китай)
Входной язык: en-us; английский (США)
Часовой пояс: (GMT + 08: 00) Пекин, Чунцин, Гонконг, Урумчи
Общая физическая память: 3,317 МБ
Доступная физическая память: 1,682 МБ
Виртуальная память: Макс. размер: 2,048 МБ
Виртуальная память: доступно: 2,007 МБ
Виртуальная память: при использовании: 41 МБ
Местоположение файла: C:\pagefile.sys
Карта (и) NetWork: Установлены 3 сетевых адаптера.
[01]: Intel(R) 82566DM-2 Gigabit Network Connection
Connection Name: Local Area Connection
DHCP Enabled: Yes
DHCP Server: 10.8.0.31
IP address(es)
[01]: 10.8.0.137
[02]: VMware Virtual Ethernet Adapter for VMnet1
Connection Name: VMware Network Adapter VMnet1
DHCP Enabled: Yes
DHCP Server: 192.168.154.254
IP address(es)
[01]: 192.168.154.1
[03]: VMware Virtual Ethernet Adapter for VMnet8
Connection Name: VMware Network Adapter VMnet8
DHCP Enabled: Yes
DHCP Server: 192.168.2.254
IP address(es)
[01]: 192.168.2.1
Любая теория объяснений? Спасибо.
Ответы
Ответ 1
Такое поведение выглядит довольно нелогичным. Интересно, что произойдет, если мы попробуем что-нибудь глупое. Если общий файл больше 512 Мбайт, вы можете снова сравнить полный 512 МБ для последней части вместо оставшегося размера.
что-то вроде:
if(remainder > 0)
{
cout << "segment size = "
<< remainder
<< " bytes for the remaining round";
if (size > segment_size){
block_size = segment_size;
offset = size - segment_size;
}
else{
block_size = remainder;
offset = segment_size * i
}
if(!segment_compare(block_size, offset)) return false;
}
Кажется, действительно глупо, потому что мы будем сравнивать часть файла два раза, но если ваши профилирования точны, это должно быть быстрее.
Это не даст нам ответа (но), но если это действительно быстрее, это означает, что ответ, который мы ищем, заключается в том, что делает ваша программа для небольших блоков данных.
Ответ 2
Насколько фрагментирован файл, с которым вы сравниваете? Вы можете использовать FSCTL_GET_RETRIEVAL_POINTERS
, чтобы получить диапазоны, которые файл сопоставляет на диске. Я подозреваю, что последние 25 МБ будут иметь множество небольших диапазонов для учета производительности, которую вы измерили.
Ответ 3
Интересно, странно ли поведение mmap ведет себя, когда сегмент не является четным числом страниц? Возможно, вы можете попробовать обработать последние части файла, постепенно уменьшая размеры своего сегмента до тех пор, пока не достигнете размера, меньшего, чем mapped_file_source:: alignment(), и обработайте это последнее немного.
Кроме того, вы говорите, что делаете блоки размером 512 МБ, но ваш код устанавливает размер 8 < 10. Затем он умножается на mapped_file_source:: alignment(). Является mapped_file_source:: alignment() действительно 65536?
Я бы рекомендовал, чтобы быть более портативным и приводить к меньшим путаницам, что вы просто используете размер, указанный в параметре шаблона, и просто требуйте, чтобы он был даже кратным mapped_file_source:: alignment() в вашем коде. Или люди проходят в силе двух, чтобы начать за размером блока, или что-то в этом роде. Имея размер блока, переданный в качестве параметра шаблона, затем умножается на некоторую странную реализацию, определенная константа кажется немного странной.
Ответ 4
Я знаю, что это не точный ответ на ваш вопрос; но попробовали ли вы пошагово устранить всю проблему - то есть просто скопируйте весь файл за один раз?
Я мало знаю об управлении памятью Win32; но в Linux вы можете использовать флаг MAP_NORESERVE
с mmap()
, поэтому вам не нужно резервировать RAM для всего файла. Учитывая, что вы просто читаете из обоих файлов, ОС должна уметь отбрасывать страницы в любое время, если ей не хватает ОЗУ...
Ответ 5
Я попробовал бы это на Linux или BSD, чтобы увидеть, как это действует, из любопытства.
У меня есть действительно грубое предположение о проблеме:
Бьюсь об заклад, что Windows делает много дополнительных проверок, чтобы убедиться, что они не отображаются в конце файла. Раньше в некоторых ОС возникали проблемы с безопасностью, которые позволяли пользователю mmap просматривать личные данные или данные файловой системы из других файлов в области, находящейся чуть ближе к концу карты, поэтому, чтобы быть осторожным, это хорошая идея для дизайнера ОС, Таким образом, Windows может использовать гораздо более тщательные "копии данных с диска на ядро, обнулять необработанные данные, копировать данные пользователю", а не намного быстрее "копировать данные с диска на пользователя".
Попробуйте сопоставить только под конец файла, исключая последние байты, которые не вписываются в блок размером 64 КБ.
Ответ 6
Может быть, вирусный сканер вызывает эти странные результаты? Вы пробовали без антивирусного сканера?
Привет,
Sebastiaan