内存复制

在计算机中,内存复制经常而普遍。它们出现在联网应用、数据库应用、科学应用以及几乎您能想得到的其它任何应用和服务中。因为它们是如此的通用,所以程序员对于内存复制有点满不在乎,而且还采用了各种各样的编程技巧来完成复制。内存复制可以是整块内存的简单移动,也可以根本不是一个复制而类似一个访问模式。

一个 访问模式的形成是通过访问一个矩阵中的各栏。比如您需要 12x12 矩阵中所有第 12 个词。这 12 个词将代表矩阵的第一栏。通常,一栏访问只需要每栏元素的 32 或 64 字节;很少多于 64 字节。复杂矩阵每栏元素可能需要两个 64 字节值。访问间的字节数被称为一个 跨距,内存访问以一个跨距值作为参数。


回页首

测量块内存的复制速度

在这个部分中,我们只观察块内存复制,这即使在第一年编程课程中也是非常普通的。测量内存复制速需要技巧,因为计算机有一级、二级和三级高速缓存(有时候有),所以测试必须考虑高速缓存的大小。我们只看最终结果,即数据移动有多快。关键在于理解代码路径长度而非系统缓冲或高速缓存的问题。这两个问题将在以后的文章中谈到。代码路径很重要,它显示字节能多快地被移动以及移动涉及的系统开销。

内存转移只需简单的编程技巧。简单的 for 循环和 memcpy() 库例程是最常用的机制。而结构分配和文件 IO 技巧则相对使用较少。

转移内存在整个性能图中只占据一小部分。当真的是占据一部分时,最好清楚操作系统和硬件作为一个组可以提供什么。

在一个栏中有太多方面要调查和涉及,因此,有必要缩小范围。这也就意味着要得到有用的结论,必须再扩大范围。一些检测参数包括:

  • 跨距 -- 如上所述,常与矩阵访问有关
  • 总体转移大小 -- 移动数据的总量
  • 块尺寸 -- 在一次单独操作中移动的数据量(与下一参数紧密有关)
  • 编程技巧 -- 如 memcpy、(char *, double *) 指针
  • 系统页大小
  • 翻译后备缓冲器 (TLB) 的大小

这里的目标是将总转移量定在 16 MB,同时将跨距定为 0,即作简单的块内存转移。块尺寸随编程技巧变化而变化。Windows 2000 下系统页大小为 4K,Linux 也是 4K。如果是相同的计算机,TLB 的大小也相同。因为只使用一个线程移动内存,所以无线程问题。


回页首

测试程序

我们使用的程序叫做 memxfer5b.cpp,它已有过几个版本。它的使用信息如下:

memxfer5b 的使用信息

            Usage: memxfer5b.exe [-f] [-w] [-s] [-p] 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 formatmethods:0:     "memcpy (default)"1:     "char *"2:     "short *"3:     "int *"4:     "long *"5:     "__int64 *"6:     "double *"

"-p" 选项之所以有用是因为在多数测试中首次复制比接下来的复制要运行地慢。即暗示高速缓存正在被装载。这里的测量法特别注意代码路径而非内存转移速度。因此,我们使用 "-p" 选项来“预先准备”内存。我们的目的是尽可能达到最快(最短时间)的内存转移。现实情况下,程序更有可能碰到的环境是,首次转移时高速缓存未做准备。尽管这样,如果我们在测试中选择能导致最佳性能的代码路径,我们就很有可能找到最优的生产代码性能。

Memxfer5b 能使用 7 种不同的内存转移技巧。“推荐的” memcpy() API 以及 6 个不同的指针类型在 Linux 和 Windows 上都可行。

Memxfer5b.cpp 可方便地编译下列任何一条命令:

gcc -O2 memxfer5b.cpp -o memxfer5b cl -O2 memxfer5b.cpp -o memxfer5b.exe

Memxfer5b.cpp 使用我在 介绍专栏中描述的相同的支持例程。在支持例程的列表中再加入一个名为 Malloc() 的例程。Malloc() 的作用和 malloc() 一样,但当无法分配内存时,它将打印错误消息并退出。就我们的目的来说,这已经足够的。我们不测量内存分配速度;这个测试中任何分配内存的失败只是说明程序里有一个错误,或者我们已经达到系统限制。这两种情况我们都不希望发生。(我的介绍专栏中也提到过 Malloc() 例程,但它不包含于任何源代码中。在这个部分中,也会提到 -- 请参阅 参考资料。)

先前,我们看了微软 C++ 编译器的各种选项,看有没有什么比 "-O2" 更好的。在我们的测试中,我们什么也没找到,于是放弃继续查找。但是,如果哪位读者在 cl.exe 用他或她喜欢的优化参数编译 memxfer5b.cpp,“并”产生更好的性能表现,请在讨论论坛上告诉我们所有人;点击文章顶部或底部的 讨论图标。群策群力会提高查找 cl.exe 参数空间的效率。

memxfer5b.cpp 的主循环是实际移动内存的部分,它通过调用定时例程将移动分类。Memxfer5b.cpp 可去块内存转移的其它区域。以后的版本将增加分离功能,这样我们就可模拟矩阵操作。


回页首

测试系统

我们将在安装了 Windows 2000 Advanced Server Service Pack 1、Linux 2.2.16 和 Linux 2.4.4 的系统编译和运行测试。这些 Linux 在 Red Hat 7.0 环境下运行。在 Linux 上,我们将使用包括在 Red Hat 7.0 发行版中的 gcc。在 Windows 2000 上,我们将使用来自 Visual Studio 6.0 的 Microsoft C++ Version 12.00.8168。我们的测试系统将是 ThinkPad 600X Model 2645-9FU,576 MB 的内存和 12 GB 的硬盘。 这个 600X 在 Windows 2000 上是个 648 MHz 奔腾 III 机器,而在 Linux 上是 647.767 MHz。Windows 2000 和 Linux 决定那信息的“机制”是:

Windows 2000 到 MHz 的浏览路径为:

Start/ Settings/ Control Panel/ Administrative Tools/ Computer Management/ System Tools/ System Information/ System Summary

Linux 显示 MHz 的命令是:

cat /proc/cpuinfo

程序以每秒兆字节为单位打印作为结果的内存速度。如果指定 "-s" 标记,那么运行 "cnt" 计算内存复制速度。否则,每次运行都被打印。我们的测试按如下方式进行:

       memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6

这是我们八次运行中的四次。不要期望看到很多变化;重复的尝试将检验我们的期待,或者给我们所要期待的加一些限定范围。


回页首

测试结果

结果显示在下表中。Linux 的新旧版本在内存转移上比 Windows 2000 显然快得多。还不清楚是怎么一回事,有待进一步研究。

memxfer5b -s -p -csv 16777216 8
方法

每秒兆字节的平均内存速度

  Linux 2.2.16-22 Linux 2.4.4

Windows 2000 AS

       
memcpy 173.644 179.417 132.077
char * 169.683 169.000 93.494
short * 170.065 172.333 96.156
int * 170.136 172.648 102.507
long * 170.066 172.050 123.498
__int64 * 170.094 172.330 123.498
double * 169.778 171.192 123.283

检查 Windows memxfer5b.exe 程序的汇编清单,发现当调用 memcpy API 时,Microsoft C++ 编译器使用 "rep movs" 指令。另外,它尽职地为 "char *" 做字符对字符移动,为 "short *" 做字对字移动,并且奇怪地为 "int *"、"long *"、"__int64 *" 和 "double *" 做双字对双字移动。(Memxfer5b 避免所有错误排列的限定条件。)

一个类似的 Linux 二进制检查显示(几乎)同样的代码。唯一例外是 gcc 实际使用 memcpy() 例程。两个编译器都只移动字节、16-bit 字和 32-bit 字。在 Linux 和 Windows 下生成的汇编代码很相似。

内存复制性能差异的一个可能性是 Windows 在后台比 Linux 做了更多的工作。为了测试这个理论,让我们写一个十分短小但计算密集的程序来计算一个单一的分形点。不带参数, fract2.cpp 重复分形公式,直到重复了 100,000,000 次或分形点“逃逸”。Fract2.cpp 是一个短浮点数(双)循环计算的简单程序。

结果如下:

操作系统 完成时间(秒)
Linux 2.2.16-22 4.065
Linux 2.4.4 4.087
Windows 2000 AS 4.300

完成时间暗示了相同硬件条件下,任何一种 linux 版本可以比 Windows 完成更多的计算。此外,当两个经过优化的编译程序被反编译后,我们发现产生的循环几乎完全一致,实际上,linux 的循环比 Windows 的多包含了两个指令。

fract2.cpp 运行时间不能说明内存复制速度上的巨大差别。而且,Fract2.cpp 可能说明了这些差别的一部分,但不是全部。


回页首

总结

就我们研究的这一点而言,我们没有足够的信息来充分理解内存复制的异常。而且,我们只涉及了块内存转移的一个很小方面。因此,还不能对 Linux 和 Windows 的优劣做出公正的结论。我们可以断定,对于 16M 字节传输,在两种平台上使用 memcpy 都是个好主意。我们也可以断定,正如 fract2.cpp 所显示的,Windows 操作系统只有一小部分的系统额外开销。然而,最好让读者自己来评价这里使用的技术,以及提出如何在这两个小测试中使每种系统运行更高效的建议。

本文提出的问题比回答的问题更多,我们将在以后的文章里更仔细地考察内存性能,在此之前,是否一个系统的块内存移动性比另一个更高仍然是个未知的问题。

参考资料

  • 请阅读开始这一专栏的 介绍文章;它定义了 Ed 使用的测量工具。
  • 获取本文提到的文件:
    • memxfer5b.cpp 是测试程序;它可使用 7 种不同的内存转移技巧。
    • memxfer5b.cpp 的主循环是真正移动内存的部分。
    • fract2.cpp 是一个短浮点数(双)循环计算的简单程序。它重复分形公式直至 100,000,000 次重复或分形点"逃逸"。
  • 在 developerWorks Linux zone 有更多的 Linux 参考资料,包括这些文章:
    • 操作系统灵活性
    • Linux,服务器操作系统

关于作者

Edward Bradf

RunTime: 块内存复制相关推荐

  1. 分布式块设备复制:客户端

    分布式块设备复制,英文名称 Distributed Replicated Block Device,简称为 DRBD,它是一种由应用引导内核驱动工作.基于网络的块复制存储解决方案,主要用于服务器之间的 ...

  2. stm32h7内存分配_【STM32H7教程】第26章 STM32H7的TCM,SRAM等五块内存的超方便使用方式...

    第26章       STM32H7的TCM,SRAM等五块内存的超方便使用方式 本章教程为大家分享一种快捷的DTCM,SRAM1,SRAM2,SRAM3和SRAM4的使用方式.MDK和IAR均支持这 ...

  3. 【Java文件操作(六)】借助内存复制图片:ByteArrayOutputStream\ByteArrayInputStream\FileOutputStream\FileInputStream辨析

    我的博客--Java文件操作系列 [Java文件操作(一)]递归打印文件目录 [Java文件操作(二)]删除文件夹,但保留其内部文件 [Java文件操作(三)]递归复制文件夹内所有文件 [Java文件 ...

  4. stm32h7内存分配_stm32h7“分散加载方式管理多块内存”

    默认情况下,我们都是通过 MDK 的 option 选项设置 Flash 和 RAM 大小,如图1 图1 这种情况下,不方便用户将变量定义到指定的 CCM 或者 SDRAM 中.而使用attribut ...

  5. 【STM32H7教程】第25章 STM32H7的TCM,SRAM等五块内存基础知识

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980  第25章       STM32H7的TCM,SRAM ...

  6. Delphi 汇编学习(十)--- 内存复制的极致优化

    在 汇编学习(八)--- 图像水平镜像垂直镜像的极致优化 中,我们使用了 FastMove,来提高内存复制速度. 但 FastMove 不支持 x64.而且很多年没有更新了.我们自己动手来写吧.由简到 ...

  7. Rt-Thread 操作系统 memheap 管理多块内存的使用方法

    文章目录 1 memheap 管理算法简介 2 只使用片内 RAM 的示例 3 配置片外 SDRAM 和 内存管理算法 4 SDRAM 的读写测试 5 内存堆申请测试 5.1 内部 RAM 和 片外 ...

  8. 13、MDK分散加载方式管理多块内存

    MDK分散加载: 默认情况下是通过MDK的option选项设置Flash和RAM大小,这种情况下所有的管理工作都是编译来处理的, MDK自动生成的分散加载文件:H7_ProjectTest.sct ; ...

  9. malloc申请得到的内存后,再free释放它的时候,操作系统会立即收回那块内存吗?

    stackoverflow上的回答: In many malloc/free implementations, free does normally not return the memory to  ...

最新文章

  1. centos vnc配置笔记
  2. 好程序员web前端CSS选择符(选择器):表示要定义样式的对象
  3. Docker 和 Kubernetes 从听过到略懂:给程序员的旋风教程
  4. 设计模式--组合(Component)模式
  5. Mysql游标循环遍历
  6. 如何扩展分布式日志组件(Exceptionless)的日志通知?
  7. python deque索引超出范围_Python基础语法
  8. 最拼爹的css属性:z-index失效情况记录
  9. 中介是如何快速炒高房租,并让你入坑的
  10. monterey系统怎么降级?macOS Monterey系统降回Big Sur的详细教程
  11. 关于黑名单和白名单的一些思考
  12. 现在70岁左右的人算不算是老年人?
  13. 【Hoxton.SR1版本】Spring Cloud Hystrix断路器
  14. 虚拟机下载-下载windows镜像
  15. 解决Unresolved external ‘AlphaBlend‘ referenced的办法
  16. 如何解决苹果Mac电脑安装PS失败并出现错误代码131 182出现“错误代码107”解决办法
  17. [详细] 搭建hexo博客并部署阿里云服务器
  18. 提个醒。阿里内网最新发布“M8”级Java面试笔记,助力金三银四
  19. 字节面试官推荐的一份 Java 基础面试题!太顶了
  20. openpnp - configure - Connect the driver to your controller

热门文章

  1. 华为matepad鸿蒙系统,预装鸿蒙系统 华为MatePad Pro 2界面曝光
  2. 技术员 Ghost Win10 x64 装机版/纯净版 201710
  3. java课题背景,办公自动化系统论文-课题研究的背景和意义及国内外发展状况.doc...
  4. Spark读HBASE - shc方案
  5. 苹果笔记本python开发第一个程序_Xcode的第一个mac程序
  6. 互联网职场常用逼格词汇
  7. 了解海外域名市场,把域名卖到全世界!
  8. java根据日期判断星座_给定公历日期计算12星座(Java代码实现)
  9. 桌面增加了IE图标无法删除
  10. C语言编程练手必备,C 语言快速实现五子棋!