Непонятное время, затраченное на использование файла с отображением памяти

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