使用内存映射文件所消耗的时间难以理解

t.g*_*.g. 10 c++ boost memory-mapped-files

我正在编写一个例程来使用内存映射文件比较两个文件.如果文件太大而无法一次映射.我拆分文件并逐个映射它们.例如,要映射1049MB文件,我将其分为512MB + 512MB + 25MB.

除了一件事之外,每件事情都运行良好:比较剩余部分(本例中为25MB)总是花费更多,更长的时间,尽管代码逻辑完全相同.3观察:

  1. 首先进行比较无关紧要,无论主要部分(512MB*N)还是剩余部分(本例中为25MB)都是第一位,结果保持不变
  2. 其余的额外时间似乎花在用户模式上
  3. VS2010 beta 1中的分析显示,时间花费在t内std::_Equal(),但这个函数主要是(分析器说100%)等待I/O和其他线程.

我试过了

  • 将VIEW_SIZE_FACTOR更改为另一个值
  • 用成员函数替换lambda仿函数
  • 更改测试中的文件大小
  • 将余数的执行顺序更改为循环之前/之后

结果非常一致:在剩余部分和用户模式中需要花费更多时间.

我怀疑它与映射大小不是映射对齐的倍数(在我的系统上为64K)这一事实有关,但不确定如何.

下面是例程的完整代码和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  
Run Code Online (Sandbox Code Playgroud)

输出:

剩余回合的段大小= 354410496字节

实际116.892s,cpu 56.201s(48.1%),用户54.548s,系统1.652s

段大小= 536870912字节,轮#0

实际72.258s,cpu 2.273s(3.1%),用户0.320s,系统1.953s

段大小= 536870912字节,第1轮

真实75.304s,cpu 1.943s(2.6%),用户0.240s,系统1.702s

段大小= 536870912字节,第2轮

真正的84.328s,cpu 1.783s(2.1%),用户0.320s,系统1.462s

段大小= 536870912字节,第3轮

真实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 = remaining - body.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; 
}                        

}
Run Code Online (Sandbox Code Playgroud)

观察:

在另外两台具有与我相同的h/w配置的PC上,结果如下:

------ VS2010Beta1ENU_VSTS.iso [1319909376 bytes] ------

剩余大小= 44840960字节

Remainder_tail size = 14336字节

实际0.060s,cpu 0.040s(66.7%),用户0.000s,系统0.040s

Remainder_body size = 44826624字节

实际13.601s,cpu 7.731s(56.8%),用户7.481s,系统0.250s

段大小= 67108864字节,总轮数#= 19

真实172.476s,cpu 4.356s(2.5%),用户0.731s,系统3.625s

但是,在具有不同h/w配置的PC上运行相同的代码会产生:

------ VS2010Beta1ENU_VSTS.iso [1319909376 bytes] ------剩余大小= 44840960字节

Remainder_tail size = 14336字节

实际0.013s,cpu 0.000s(0.0%),用户0.000s,系统0.000s

Remainder_body size = 44826624字节

真正的2.468s,cpu 0.188s(7.6%),用户0.047s,系统0.141s

段大小= 67108864字节,总轮数#= 19

真实的65.587s,cpu 4.578s(7.0%),用户0.844s,系统3.734s

系统信息

我的工作站产生了难以理解的时间:

操作系统名称:Microsoft Windows XP Professional

操作系统版本:5.1.2600 Service Pack 3 Build 2600

OS制造商:Microsoft Corporation

操作系统配置:成员工作站

操作系统构建类型:单处理器免费

原始安装日期:2004-01-27,23:08

系统运行时间:3天,2小时,15分钟,46秒

系统制造商:戴尔公司

系统型号:OptiPlex GX520

系统类型:基于X86的PC

处理器:已安装1个处理器.

                       [01]: x86 Family 15 Model 4 Stepping 3 GenuineIntel ~2992 Mhz
Run Code Online (Sandbox Code Playgroud)

BIOS版本:DELL - 7

Windows目录:C:\ WINDOWS

系统目录:C:\ WINDOWS\system32

引导设备:\ Device\HarddiskVolume2

系统区域设置:zh-cn;中文(中国)

输入区域:zh-cn;中文(中国)

时区:(格林尼治标准时间+08:00)北京,重庆,香港,乌鲁木齐

物理内存总量:3,574 MB

可用物理内存:1,986 MB

虚拟内存:最大大小:2,048 MB

虚拟内存:可用:1,916 MB

虚拟内存:正在使用:132 MB

页面文件位置:C:\ pagefile.sys

网络卡:已安装3个NIC.

       [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
Run Code Online (Sandbox Code Playgroud)

另一个工作站产生"正确"的时间:操作系统名称:Microsoft Windows XP Professional

操作系统版本:5.1.2600 Service Pack 3 Build 2600

OS制造商:Microsoft Corporation

操作系统配置:成员工作站

操作系统构建类型:多处理器免费

原始安装日期:2009年5月18日,下午2:28:18

系统运行时间:21天,5小时,0分钟,49秒

系统制造商:戴尔公司

系统型号:OptiPlex 755

系统类型:基于X86的PC

处理器:已安装1个处理器.

        [01]: x86 Family 6 Model 15 Stepping 13 GenuineIntel ~2194 Mhz
Run Code Online (Sandbox Code Playgroud)

BIOS版本:DELL - 15

Windows目录:C:\ WINDOWS

系统目录:C:\ WINDOWS\system32

引导设备:\ Device\HarddiskVolume1

系统区域设置:zh-cn;中文(中国)

输入区域设置:en-us;英语(美国)

时区:(格林尼治标准时间+08:00)北京,重庆,香港,乌鲁木齐

物理内存总量:3,317 MB

可用物理内存:1,682 MB

虚拟内存:最大大小:2,048 MB

虚拟内存:可用:2,007 MB

虚拟内存:正在使用:41 MB

页面文件位置:C:\ pagefile.sys

网络卡:已安装3个NIC.

       [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
Run Code Online (Sandbox Code Playgroud)

任何解释理论?谢谢.

MSN*_*MSN 2

您要比较的文件的碎片程度如何?您可以使用它FSCTL_GET_RETRIEVAL_POINTERS来获取文件在磁盘上映射到的范围。我怀疑最后 25 MB 会有很多小范围来说明您测量的性能。