`
netcome
  • 浏览: 466712 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

运行时: 块内存复制,第 2 部分

阅读更多

我的 前一专栏专注于 16-MB 的内存块,这次我将讨论大小范围在 4 字节到 64 MB 之间的内存块。先前,我检验了各种执行内存传送的方法并确定使用系统提供的 memcpy() 例程是一个很不错的主意(至少在学会其它更好的方法之前,我会一直这样认为)。

在此处描述的测试中,我运行了几次传送以确保数据是可再生的。我的测试仅在内存为 576 MB 的 ThinkPad 600X (650 MHz) 上运行。没有在其它的双重引导系统上运行过。

我鼓励您在双重引导机器上运行这些测试并报告测试结果。另外,我还鼓励您对程序中的编程方法提出批评和提高性能的建议。本文中,我们的目的是演示最好的编程实践,而不是为证明一个系统比另一个好。

测试内存复制时间

我将使用与上个月几乎相同的程序。仍然使用一个开销小的、简单的源代码管理系统,我将程序重命名为 memxfer5c.cpp

我所做的更改考虑到了小于 32 个字节的内存块。由于部分循环展开是通过 "double *" 传送进行,所以上个月的程序只允许传送大于或等于 32 个字节。Memxfer5c.cpp 只是仅仅不用 "double *" 方法传送小于 32 字节的内存块。另外,还纠正了用法信息中的一个错误。

现在,让我们检查一下块大小和编程技术。我们的测试运行的测试脚本是 test2c.sh。它主要由一长列带有不同块大小和循环计数的 memxfer5c 命令行组成。

Memxfer5c.cpp 的用法信息几乎相同:


memxfer5c 用法信息
Usage:memxfer5c.exe [-f] [-w] [-s] [-p] [-csv] size cnt [method]
        -f flag says to malloc and free of the "cnt" times.
        -w = set process min and max working set size to "size"
        -s = silent; only print averages
        -p = prep; "freshen" cache before; -w disables
        -csv = print output in CSV format
        methods:
         0:     "memcpy (default)"
         1:     "char *"
         2:     "short *"
         3:     "int *"
         4:     "long *"
         5:     "__int64 *"
         6:     "double *"

测试脚本文件中一个典型的命令行如下所示:

     
	  memxfer5c -csv -s -p 4 64m 0 1 2 3 4 5 6

这条命令的意思是按用逗号隔开的列表格式( "-csv" )输出数据,汇总信息( "-s" ),“准备”高速缓存( "-p" ),使用 4 字节传送,传送 6400 万次,并为用法信息中列出的每种方法执行这种测试。

Memxfer5c.cpp 使用以下命令进行编译:

     gcc -O2 memxfer5c.cpp -o memxfer5c
    或
     cl -O2 memxfer5c.cpp -o memxfer5c.exe

(注意:本系列的 介绍专栏中有对 memxfer5c.cpp 使用的支持例程的描述。)

既然 memxfer5c.cpp 程序可用,而且与上个月的程序几乎没什么差别,所以就不再提供程序清单。我将显示测试运行的图形化结果。使用以下命令运行这个测试:

bash test2c.sh a

参数 a 是一个讨厌的小东西,它强迫我考虑这个测试做什么工作。如果脚本不带参数运行,它只在打印一条用法信息后便退出。任何参数都会起作用。

我们在配备 576 MB 内存和 12-GB 硬盘的 ThinkPad 600X Model 2645-9FU 上运行此脚本。600X 是一台 648 MHz 的 Pentium III 机器。(我的 前一专栏展示了如何在 Windows 和 Linux 下查找处理器型号和以 MHz 为单位的速率。) 测试过的操作系统有:

  • Linux 2.2.16-22
  • Linux 2.4.4
  • Windows 2000 SP1

由于 Linux 内核存在于 Red Hat 7.0 环境中,所以我们使用包含在 Red Hat 7.0 分发版(版本 2.96)中的 gcc。在 Windows 2000 上,我们使用 Visual Studio 6.0 中的 Microsoft C++(版本 12.00.8168)。

运行的一次典型输出 ― 展开各栏后 ― 如下所示:

    memxfer5c.exe -s -p -csv 4 67108864 Win2k
        "memcpy", 4,268435456,   5.862,  45.789
        "char *", 4,268435456,   3.243,  82.771
        "short *",4,268435456,   3.139,  85.507
        "int *",  4,268435456,   2.721,  98.667
        "long *", 4,268435456,   2.720,  98.672
        "memcpy", 4,268435456,   5.862,  45.795
        "memcpy", 4,268435456,   5.858,  45.828

只有信息中的第 1、第 2、和最后一栏在绘图时被用到。另外两栏被用于验证最后一栏的正确性。例如,从上面的“memcpy”行,我们可以看到:

		45.792 = 268435456/(5.862*1E6)

我使用的是舍位过的数字,可以验证数字 45.792 的正确性,精确到四位有效数字。对于绘图工作,这已经足够了。

看一下数据。前 3 个图(用 Microsoft Excel 制作的)分别代表了每种操作系统上的运行,并显示了这些系统间非常相似的表现。


图 1. Linux 2.2.16-22
Linux 2.2.16-22 

图 2. Linux 2.4.4
Linux 2.4.4 

图 3. Win2k
Win2k 

我还为每个方法绘了一张图,用来显示不同的操作系统:


图 4. Linux 和 Win2k 中的 Memcpy
Memcpy Linux 和 Win2k 

图 5. Linux 和 Win2k 中的 "Char"
Char Linux 和 Win2k 

图 6. Linux 和 Win2k 中的 "Short"
Short Linux 和 Win2k 

图 7. Linux 和 Win2k 中的 "Int"
Int Linux 和 Win2k 

图 8. Linux 和 Win2k 中的 "Long"
Long Linux 和 Win2k 

图 9. Linux 和 Win2k 中的 "_int64"
_int64 Linux 和 Win2k 

图 10. Linux 和 Win2k 中的 "Double"
Double Linux 和 Win2k 

这些图有一些古怪的地方。首先,在 Linux 上使用 "long *" 好象比使用 "int *" 更好。既然 int 和 long 变量在 Linux 和 Windows 上的长度相同,肯定是什么地方出错了。main(方法 3 和方法 4 )的相关部分的反汇编如下所示:


方法 3 和方法 4("int *" 和 "long *")反汇编
    0x8048ec0 :  xor    %esi,%esi
    0x8048ec2 :  cmp    %edi,%esi
    0x8048ec4 :  mov    0xffffffdc(%ebp),%ecx
    0x8048ec7 :  mov    0xffffffd8(%ebp),%edx
    0x8048eca :  jae    0x8048f87 
    0x8048ed0 :  mov    (%edx),%eax
    0x8048ed2 :  add    $0x4,%esi
    0x8048ed5 :  mov    %eax,(%ecx)
    0x8048ed7 :  add    $0x4,%edx
    0x8048eda :  add    $0x4,%ecx
    0x8048edd :  cmp    %edi,%esi
    0x8048edf :  jb     0x8048ed0 
    long *
    0x8048ee8 :  xor    %esi,%esi
    0x8048eea :  cmp    %edi,%esi
    0x8048eec :  mov    0xffffffdc(%ebp),%ecx
    0x8048eef :  mov    0xffffffd8(%ebp),%edx
    0x8048ef2 :  jae    0x8048f87 
    0x8048ef8 :  mov    (%edx),%eax
    0x8048efa :  add    $0x4,%esi
    0x8048efd :  mov    %eax,(%ecx)
    0x8048eff :  add    $0x4,%edx
    0x8048f02 :  add    $0x4,%ecx
    0x8048f05 :  cmp    %edi,%esi
    0x8048f07 :  jb     0x8048ef8 

如您所见,代码是同样的。我将 memxfer5c.cpp 源文件复制到 x.cpp,并开始忙于一些琐事。我首先做的是验证正确的方法确实被执行了。然后,我尝试切换方法 3 和方法 4 的顺序,结果是 "int *" 的速度变快了,而 "long *" 的速度却变慢了。使用相同的两个值,但通过交换代码的位置,性能也交换了。我得出的唯一结论就是代码一定是处于高速缓存的边界上。就象我们在上面看到的,没有两部分代码交叉的页边界。

第 2 点神秘之处是与 Windows 比较时 Linux 在 "int *" 和 "long *" 上的表现。在使用 "char *" 、 "short *" 和 "int *" 时,Windows 比例适中。每张图显示在内存传送性能方面 Windows 大约是 Linux 的两倍。Linux 在进行 4 字节传送时速度达到 600 MB/秒左右便停顿,无法再上升。这种现象的原因委实不清楚。

另外让人觉得好奇的一点就是 Linux 和 Windows 的 memcpy 图中的“锯齿状图形”。当使用的块大小在 4K 到 32K 之间时,Linux 图中就会出现这种“锯齿状图形”。这里是这个区域(锯齿状图形)的放大后的图像,没有使用块大小轴上的记录刻度。


图 11. Linux 和 Win2k 中的 Memcpy
 

这种表现看起来象是高速缓存级别之间的差频。系统设计者可能不得不解释为什么 Linux 会这样。





回页首


关于编译器优化的注意事项

读者 Matteo Ianeselli 指出了 gcc 编译器的“-funroll-loops”选项。因为我们输入限制循环次数的变量,所以我不清楚编译器如何展开这些循环中的一个循环。根据我在命令行输入的值,编译器可能已经把循环过分展开。我使用“-funroll-loops”选项在 ThinkPad 770X 上运行一个快速测试。结果如下所示:

  • 对于小于 1.5 MB 的传送,展开循环比不使用这个选项展开的速度稍慢。
  • 对于大于 1.5 MB 的传送,展开循环产生的加速大约是 135/131。

我先前已经看过 Microsoft C++ 编译器上的各种选项以查看是否有比“-O2”更好的选项。在我的测试中,我没有发现。我又放弃了继续搜索。但是,如果我们的读者用他/她最喜欢的 cl.exe 上的优化参数编译 memxfer5c.cpp,“并且”使性能得到了提高,请通过 讨论论坛告诉我们。当众多人参与时,搜索 cl.exe 的参数空间的效率就会更高。





回页首


结论

这次好象我提出的问题比答案还多。除上面提出的奇怪现象外,我的结果看起来都是在预料之中的。

先前,我慎重地下了结论:在 Linux 和 Windows 下使用 memcpy() 是一个好主意。这个月的测量肯定了这个结论。Memcpy() 产生的结果比 Windows 和 Linux 上任何其它的方法产生的结果都要好。将 Windows 与 Linux 相比较,Windows 在使用 4 字节指针传送内存和传送小于 200 KB 内存时速度更快。传送的内存小于 10 KB 时,局部展开的 "double *" 方法在 Windows 上也更快。在所有其它的情况下,Linux 移动内存的速度好象更快。



参考资料



关于作者

Edward Bradford 博士现在为 IBM Software Group 管理 Microsoft Premier Support,并且每周

分享到:
评论

相关推荐

    智慧学院智能化项目规划设计方案PPT(45页).pptx

    智慧学院智能化项目规划设计方案PPT(45页).pptx

    AO工艺设计计算(全).xls

    污水处理计算书

    ASP+ACCESS动态网站设计与制作(源代码+设计说明书).zip

    ASP+ACCESS动态网站设计与制作(源代码+设计说明书).zip

    基于matlab实现的二维渗流代码,用于模拟在二维条件下,格点所受碰撞的次数.rar

    基于matlab实现的二维渗流代码,用于模拟在二维条件下,格点所受碰撞的次数.rar

    基于matlab实现可实现脉冲编码调制,模拟信号的数字传输:抽样、量化、编码.rar

    基于matlab实现可实现脉冲编码调制,模拟信号的数字传输:抽样、量化、编码.rar

    ASP+access网上购物系统(设计说明书+源代码).zip

    ASP+access网上购物系统(设计说明书+源代码).zip

    Screenshot_20240503_054519.jpg

    Screenshot_20240503_054519.jpg

    node-v12.17.0-linux-armv7l.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    ASP+ACCESS软件信息发布系统设计(源代码+设计说明书+调研报告+任务书).zip

    ASP+ACCESS软件信息发布系统设计(源代码+设计说明书+调研报告+任务书).zip

    2024年金属氧化物避雷器行业分析报告.pptx

    行业报告

    ASP+SQL学生排课管理系统(源代码+设计说明书).zip

    ASP+SQL学生排课管理系统(源代码+设计说明书).zip

    node-v10.18.1-linux-ppc64le.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    The Experiment 2 of Engineering Electromagnetics.pdf

    The Experiment 2 of Engineering Electromagnetics.pdf

    2003-2022年上市公司管理层权力强度综合指标含原始数据+do代码(excel+dta)

    2003-2022年上市公司管理层权力强度综合指标含原始数据+do代码(exce l+dta) 作为公司内部监督的一个重要方面,有效的内部控制能够减轻管理层与股东 之间的代理冲突,进一步保护投资者利益; 而作为公司外部监督的一个重要内容, 分析 师跟踪有助于降低管理层与外部投资者之间的信息不对称程度, 提高公司的可信度。这两 者都可以在一定程度上有效约束管理层的机会主义行为。 1、数据时间:2000-20 22年 2、数据文件:包含原始数据、do代码、参考文献及最终结果 3、数据格式: excel+dta格式 4、数据说明: ①剔除了ST、*ST、金融业、资不抵债、 关键数据缺失的观测值; ②参考了郑珊珊(2019)和刘剑民(2019)的方法,运 用主成分分析方法,构建了2个管理层权力强度的指标。 ③Powe_郑珊珊:依据两职 合一、董事会规模、 内部董事比例、股权分散度、管理层持股,运用主成分分析方法构建 而成。 ④Powe_刘剑民:依据两职合一、董事会规模、 内部董事比例、总经理任期 、管理层持股,运用主成分分析方法构建而成。 5、数据预览: 数据文件: do代码 : 运行结果:

    基于matlab实现的进行渗流力学的s-w曲线,压降漏斗,相渗曲线的作图.rar

    基于matlab实现的进行渗流力学的s-w曲线,压降漏斗,相渗曲线的作图.rar

    基于地理格网的时空知识图谱python源码+详细说明.zip

    如何运行及使用 运行 用eclipse直接clone本项目,GettingStarted目录下的GettingStarted类可以直接运行,查看结果 同时jar目录下有生成的jar包,下载后也可以直接调用 使用 初次使用流程:新建知识图谱加载知识图谱添加数据添加或查询 非初次:加载知识图谱添加或查询 Geokg包中主要类与方法介绍 KnowledgeGraph类 创建知识图谱方法 调用创建图谱方法,则会在指定目录创建数据库,一个目录下只能创建一个知识图谱,否则程序报错并强制退出 创建知识图谱的方法有两个,都为静态方法,可以通过类名KnowledgeGraph直接调用,分别为: //@param iGridLevel 要构建的知识图谱网格的等级,取值范围为0-20,小于0取自动取0,大于20自动取20 //@param strDataStore 自定义的存储知识图谱的本地目录 public static boolean createKnowledgeGraph(int iGridLevel,String strDataStore){} //@param iGri

    基于matlab实现的应用于配电网重构的改进二进制粒子群算法,能够取得比较理想的效果.rar

    基于matlab实现的应用于配电网重构的改进二进制粒子群算法,能够取得比较理想的效果.rar

    node-v12.22.6-linux-armv7l.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    商业分析工具:常用战略分析工具gl.ppt

    商业分析工具:常用战略分析工具gl.ppt

    氧化沟工艺在污水处理中的应用.doc

    课程设计污水处理

Global site tag (gtag.js) - Google Analytics