c++ - 使用内存映射文件所花费的时间无法理解

标签 c++ boost memory-mapped-files

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

除一件事外,其他所有事物都可以正常工作:比较其余部分(本示例中为25MB)通常会花费很多,更长的时间,尽管代码逻辑是完全相同的。 3个观察结果:

  • 先比较哪个都无所谓,无论是主要部分(512MB * N)还是其余部分(在本示例中为25MB)排在最前面,结果保持不变
  • 其余的多余时间似乎是在用户模式下花费的
  • 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  
    

    输出:

    段大小= 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 和尾部(剩余= body +尾部),其中
  • 主体= N *对齐(),尾部<1 *对齐()
  • 主体= m *对齐(),并且尾部<1 *对齐()+ n *对齐(),其中m为偶数。
  • 体= m *对齐(),尾部<1 *对齐()+ n *对齐(),其中m是2的指数。
  • body = N *对齐方式(),尾部=余数- body 。 N是随机的。

  • 总时间保持不变,但是我可以看到时间不一定与尾部有关,而与 body 和尾部的大小有关。更大的部分需要更多的时间。时间是USER TIME,这对我来说是最不可理解的。

    我也通过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台与我的硬件配置相同的PC上,结果保持一致,如下所示:

    ------ VS2010Beta1ENU_VSTS.iso [1319909376字节] ------

    剩余大小= 44840960字节

    Remainder_tail大小= 14336字节

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

    Remainder_body大小= 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

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

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

    Remainder_tail大小= 14336字节

    实际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

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

    系统信息

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

    操作系统名称:Microsoft Windows XP Professional

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

    操作系统制造商:Microsoft Corporation

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

    操作系统内部版本类型:单处理器免费

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

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

    系统制造商:Dell Inc.

    系统型号:OptiPlex GX520

    系统类型:基于X86的PC

    处理器:已安装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 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
    

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

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

    操作系统制造商:Microsoft Corporation

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

    操作系统内部版本类型:多处理器免费

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

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

    系统制造商:Dell Inc.

    系统型号:OptiPlex 755

    系统类型:基于X86的PC

    处理器:已安装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 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
    

    有什么解释理论吗?谢谢。

    最佳答案

    这种行为看起来很不合逻辑。我想知道如果我们尝试一些愚蠢的事情会怎样。如果整个文件大于512MB,则可以再比较最后一部分的完整512MB,而不是剩余大小。

    就像是:

            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;    
            }   
    

    这样做似乎很愚蠢,因为我们会将文件的一部分与进行两次比较,但是如果您的分析数据准确,它应该会更快。

    它不会给我们答案(但是),但是如果确实更快,则意味着我们正在寻找的响应取决于您的程序对小块数据的处理方式。

    关于c++ - 使用内存映射文件所花费的时间无法理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1371996/

    相关文章:

    c# - 如何让 IntPtr 访问 MemoryMappedFile 的 View ?

    c++ - circular_buffer 和 managed_mapped_file 段错误

    node.js - 我将如何为 node.js 设计和实现非阻塞内存映射模块

    struct - 升压凤凰: Binding to reference members of structures?

    c++ - 如何在ascii值超过127的字符串中找到8位子字符串?

    c++ - 解释c中的头文件路径

    c++ - boost::enable_if_c 错误:不是模板非类型参数的有效类型

    c++ - 绝对值/减法

    windows - boost::random_device 的非 Linux 实现

    c++ - 在扩展模块中为用户定义的 python 类 boost Python to_python_converter