内存拷贝的优化方法(1)[转]

内存零拷贝详解 目录 前置知识 标准设备 标准协议 利用中断减少CPU开销 利用DMA进行继续优化 零拷贝 零拷贝之mmap 零拷贝之sendfile 前置知识 标准设备 一个标准的硬件设备包括两部分: 面向系统其他部分展现的硬件接口(类似于软件的接口),供其他硬件或软件调用 内部结构,包括设备相关的特定实现,负责具体实现设备展示给系统的抽象接口 标准协议 一个标准设备的接口包含三个寄存器,通过读写这些寄存器,操作系统可以控制设备的行为: 一个状态(status)寄存器,可以读取并.. 阅读详情
 在复杂的底层网络程序中,内存拷贝、字符串比较和搜索操作很容易成为性能瓶颈所在。编译器自带的此类函数虽然做了一些通用性的优化工作,但因为在使用指令集方面受到兼容性的约束,远远没有达到最大限度利用硬件能力的地步。而通过针对特定硬件平台的优化,可以大大提高此类操作的性能。下面我将以P4平台下内存拷贝操作为例,根据AMD提供的一份优化文档中的例子,简要介绍一下如何通过特定指令集,优化内存带宽的使用。虽然因为硬件限制没有达到AMD文档中所说memcpy函数300%的性能提升,但在我机器上实测也有%175-%200的明显性能提升(此数据可能根据机器情况不同)。

     Optimizing Memory Bandwidth from AMD

     按照众所周知的“摩尔”定律,CPU的运算速度每18个月翻一翻,但与此同时内存和外存(硬盘)的速度并无法达到同步增长。这就造成高速CPU与相对低速的内存和外设之间的不同步发展,成为很多程序的瓶颈所在。而如何最大限度提升对现有硬件的利用程度,是算法以下层面优化的主要途径。对内存拷贝操作来说,了解和合理使用Cache是最关键的一点。为追求性能,我们将以牺牲兼容性为代价,因此以下讨论和代码都以P4及以上级别CPU为主,AMD芯片虽然实现上有所区别,但在指令集和整体结构上相同。

     首先我们来看一个最简单的memcpy的汇编实现:
以下为引用:

;
; Flier Lu (flier@nsfocus.com)
;
; nasmw.exe -f win32 fastmemcpy.asm -o fastmemcpy.obj
;
; extern "C" {
;    extern void fast_memcpy1(void *dst, const void *src, size_t size);
; }
;
cpu p4

segment .text use32

global _fast_memcpy1

%define param        esp+8+4
%define src          param+0
%define dst          param+4
%define len          param+8

_fast_memcpy1:
   push esi
   push edi

   mov esi, [src]               ; source array
   mov edi, [dst]               ; destination array
   mov ecx, [len]

   rep movsb

   pop edi
   pop esi
   ret


     这里我为了代码可移植性,使用的是 NASM格式的汇编代码。 NASM是一个非常出色的开源汇编编译器,支持各种平台和中间格式,被开源项目广泛使用,这样可以避免同时使用 VC 的嵌入式汇编和 GCC 中麻烦的 unix 风格 AT&T 格式汇编

     代码初始的cpu p4定义使用p4指令集,因为后面的很多优化工作使用了P4指令集和相关特性;接着的segment .text use32定义此代码在32位代码段;然后global定义标签_fast_memcpy1为全局符号,使得C++代码中可以LINK其.obj后访问此代码;最后%define定义多个宏,用于访问函数参数。

     在C++中只需要定义fast_memcpy1函数格式并链接nasm编译生成的.obj文件即可。NASM编译时 -f 参数指定生成中间文件格式为 MS 的 32 位 COFF 格式,-o 参数指定输出文件名。

     上面这段代码非常简单,适合小内存块的快速拷贝。实际上VC编译器在处理小内存拷贝时,会自动根据情况使用 rep movsb 直接替换 memcpy 函数,通过忽略函数调用和堆栈操作,优化代码长度和性能。

     不过在 32 位的 x86 架构下,完全没有必要逐字节进行操作,使用 movsd 替换 movsb 是必然的选择。
以下为引用:

global _fast_memcpy2

%define param        esp+8+4
%define src          param+0
%define dst          param+4
%define len          param+8

_fast_memcpy2:
   push esi
   push edi

   mov esi, [src]               ; source array
   mov edi, [dst]               ; destination array
   mov ecx, [len]
   shr ecx, 2                   ; convert to DWORD count

   rep movsd

   pop edi
   pop esi
   ret


     为了展示方便,这里假设源和目标内存块本身长度都是64字节的整数倍,并且已经4K页对齐。前者保证单条指令不会出现跨CACHE行访问的情况;后者保证测试速度时不会因为跨页操作影响测试结果。等会分析CACHE时再详细解释为什么要做这种假设。

     不过因为现代CPU大多使用了很长的指令流水线,多条指令并行工作往往比一条指令效率更高,因此 AMD 文档中给出了这样的优化:
以下为引用:

global _fast_memcpy3

%define param        esp+8+4
%define src          param+0
%define dst          param+4
%define len          param+8

_fast_memcpy3:
   push esi
   push edi

   mov esi, [src]               ; source array
   mov edi, [dst]               ; destination array
   mov ecx, [len]
   shr ecx, 2                   ; convert to DWORD count

.copyloop:
   mov eax, dword [esi]
   mov dword [edi], eax

   add esi, 4
   add edi, 4

   dec ecx
   jnz .copyloop

   pop edi
   pop esi
   ret


     标签.copyloop中那段循环实际上完成跟rep movsd指令完全相同的工作,但是因为是多条指令,理论上CPU指令流水线可以并行处理之。故而在AMD的文档中指出能有1.5%的性能提高,不过就我实测效果不太明显。相对而言,当年从486向pentium架构迁移时,这两种方式的区别非常明显。记得Delphi 3还是4中就只是通过做这一种优化,其字符串处理性能就有较大提升。而目前主流CPU厂商,实际上都是通过微代码技术,内核中使用RISC微指令模拟CISC指令集,因此现在效果并不明显。

     然后,可以通过循环展开的优化策略,增加每次处理数据量并减少循环次数,达到性能提升目的。
以下为引用:

global _fast_memcpy4

%define param        esp+8+4
%define src          param+0
%define dst          param+4
%define len          param+8

_fast_memcpy4:
   push esi
   push edi

   mov esi, [src]               ; source array
   mov edi, [dst]               ; destination array
   mov ecx, [len]
   shr ecx, 4                   ; convert to 16-byte size count

.copyloop:
   mov eax, dword [esi]
   mov dword [edi], eax

   mov ebx, dword [esi+4]
   mov dword [edi+4], ebx

   mov eax, dword [esi+8]
   mov dword [edi+8], eax

   mov ebx, dword [esi+12]
   mov dword [edi+12], ebx

   add esi, 16
   add edi, 16

   dec ecx
   jnz .copyloop

   pop edi
   pop esi
   ret


     但这种操作就 AMD 文档上评测反而有 %1.5 性能降低,呵呵。其自己的说法是需要将读取内存和写入内存的操作分组,以使CPU可以一次性搞定。改称以下分组操作就可以比_fast_memcpy3提高3% -_-b
以下为引用:

global _fast_memcpy5

%define param        esp+8+4
%define src          param+0
%define dst          param+4
%define len          param+8

_fast_memcpy5:
   push esi
   push edi

   mov esi, [src]               ; source array
   mov edi, [dst]               ; destination array
   mov ecx, [len]
   shr ecx, 4                   ; convert to 16-byte size count

.copyloop:
   mov eax, dword [esi]
   mov ebx, dword [esi+4]
   mov dword [edi], eax
   mov dword [edi+4], ebx

   mov eax, dword [esi+8]
   mov ebx, dword [esi+12]
   mov dword [edi+8], eax
   mov dword [edi+12], ebx

   add esi, 16
   add edi, 16

   dec ecx
   jnz .copyloop

   pop edi
   pop esi
   ret


     可惜我在P4上实在测不出什么区别,呵呵,大概P4和AMD实现流水线的思路有细微的出入吧

     既然进行循环展开,为什么不干脆多展开一些呢?虽然x86下面通用寄存器只有那么几个,但是现在有MMX啊,呵呵,大把的寄存器啊 改称使用MMX寄存器后,一次载入/写入操作可以处理64字节的数据,呵呵,比_fast_memcpy5可以再有7%的性能提升。
以下为引用:

global _fast_memcpy6

%define param        esp+8+4
%define src          param+0
%define dst          param+4
%define len          param+8

_fast_memcpy6:
   push esi
   push edi

   mov esi, [src]               ; source array
   mov edi, [dst]               ; destination array
   mov ecx, [len]               ; number of QWORDS (8 bytes) assumes len / CACHEBLOCK is an integer
   shr ecx, 3

   lea esi, [esi+ecx*8]         ; end of source
   lea edi, [edi+ecx*8]         ; end of destination
   neg ecx                      ; use a negative offset as a combo pointer-and-loop-counter

.copyloop:
   movq mm0, qword [esi+ecx*8]
   movq mm1, qword [esi+ecx*8+8]
   movq mm2, qword [esi+ecx*8+16]
   movq mm3, qword [esi+ecx*8+24]
   movq mm4, qword [esi+ecx*8+32]
   movq mm5, qword [esi+ecx*8+40]
   movq mm6, qword [esi+ecx*8+48]
   movq mm7, qword [esi+ecx*8+56]

   movq qword [edi+ecx*8], mm0
   movq qword [edi+ecx*8+8], mm1
   movq qword [edi+ecx*8+16], mm2
   movq qword [edi+ecx*8+24], mm3
   movq qword [edi+ecx*8+32], mm4
   movq qword [edi+ecx*8+40], mm5
   movq qword [edi+ecx*8+48], mm6
   movq qword [edi+ecx*8+56], mm7

   add ecx, 8
   jnz .copyloop

   emms

   pop edi
   pop esi

   ret


     优化到这个份上,常规的优化手段基本上已经用尽,需要动用非常手段了,呵呵。
用汇编实现memcpy函数 阅读详情

相关推荐

STM32 串口采用DMA方式收发_if(buffer) memcpy((1)

流程:串口发送数据,全部数据发送完毕后,会产生一个发送中断,所以。9)使能或关闭DMA通道的内存到内存传输。2)定义DMA通道的DMA缓存的大小。对应我的例子,就是送快递还是取快递。ps: 即货柜大小,能存多少个快件。1) 串口初始化 — 使用串口一。1)数据传输的目的地和来源。3)外设地址寄存器递增与否。4)内存地址寄存器递增与否。7)设置了DMA的工作模式。8)DMA通道的软件优先级。5)设定了外设数据宽度。6)设定了内存数据宽度。发送数据分为两部分,

2401_85012467的博客 849

#pragma pack(1) 的意义是什么

设置结构体的边界对齐为1个字节,也就是所有数据在内存中是连续存储的。 比如你在C语言中定义下面这样的结构体: struct s { char ch; int i; }; 然后在主函数中写一句:printf("%d", sizeof(struct s)) 也就是输出结构体s所占的字节数 你觉得输出结果会是多少呢? 我们知道,char型占用1个字节,int型占4个字节,那么输出的结果是5吗

程序猿的梦想---牛逼的程序猿 1036

memcpy内存拷贝及优化策略图解

#include <iostream> using namespace std; //不安全的内存拷贝(当源内存地址与目标内存地址重叠时会产生错误) void h_memcpy(void*src,void *dst,intsize){ if (src == NULL|| dst == NULL) { return; }

天天爱增肥 3594

STM32基础/memcpy

memcpy

KingOne007的博客 1293

【优化系列】arm架构32位汇编优化总结

Date: 2018.8.18 文章目录1、参考:2、Arm汇编架构和Reference Manuals3、Arm32位寄存器4、ARM指令寻址方式5、ARM指令特点以及优化技巧6、ARM和NEON指令集 常用指令汇总7、Arm32位加载数据的两种格式8、Arm32位汇编编写demo9、注意事项10、汇编优化基本准则11、ARM程序调试 1、参考: ARM参考手册 【arm】arm优化基本知识(寄存器、指令集、调用规则以及汇编格式) 【arm】ARM寄存器以及使用说明 【arm】arm的汇编指令及特点 【

优快云明星博主,认证博客专家,视频、Matlab领域优质创作者。 2705

协方差矩阵

对两个类内协方差矩阵进行对角化,可以将它们变换为对角矩阵。这个过程可以通过对每个协方差矩阵进行特征值分解来实现,从而得到它们的特征向量和特征值,进而得到它们的对角矩阵形式。这个过程的意义在于,它可以帮助我们找到一个新的坐标系,使得在这个坐标系下,每个特征之间都是独立的,从而简化了计算。具体来说,当我们使用对角协方差矩阵作为特征向量的坐标系时,它意味着每个特征之间都是相互独立的,从而方便了进一步的分析和处理。在实际应用中,对角化协方差矩阵可以用于降维、特征选择和分类等任务。

m0_55196097的博客 1万+

内存拷贝优化(1)-小内存拷贝优化

<br />相信大家代码里有很多地方用到memcpy这个函数,相信这个函数的占用是不小的,有时优化了memcpy,能使整个项目的运行效率提升。通过适当的编码技巧,让我们的内存拷贝速度超过memcpy两倍,是可以实现的。<br /> <br />有人说memcpy还能优化么?不就是rep movsd么?CPU和内存之间的带宽是固定的,怎么可能优化呢?其实是普通的内存拷贝并没有发挥全部的带宽,很多被浪费掉了,比如要等到数据完全读取成功后再去写入,然后要写入成功后再去读取新的。而优化本身就是使这两者尽量的并行。发

Skywind Inside 5812

Day2 十分钟带你解决逆向中对称加密算法 (RC4)

在密码学中,RC4(来自Rivest Cipher 4的缩写)是一种串流加密算法,密钥长度可变。他加密解密使用相同的密钥,因此也被称为“流加密算法”。RC4由伪随机数生成器和异或运算组成。RC4的密钥长度可变,范围是[1,255]。RC4一个字节一个字节地加解密。给定一个密钥,伪随机数生成器接受密钥并产生一个S盒。S盒用来加密数据,而且在加密过程中S盒会变化。由于异或运算的对合性,RC4加密解密使用同一套算法。

m0_73779708的博客 1289

内存拷贝的优化方法

http://www.blogcn.com/blog/cool/main.asp?uid=flier_lu&id=1577430http://www.blogcn.com/blog/cool/main.asp?uid=flier_lu&id=1577440在复杂的底层网络程序中,内存拷贝、字符串比较和搜索操作很容易成为性能瓶颈所在。编译器自带的此类函数虽然做了一些通用性的优化工作,但因为在使用

Flier's Sky 2825

滞回比较器电路详细分析

滞回比较器是一种用于检测两个不同电压值并使比较器输出高电平或低电平的电路。滞回比较器的特点是具有迟滞特性,即当输入电压高于某一阈值时,比较器输出高电平,当输入电压低于另一阈值时,比较器输出低电平,而在这两个阈值之间,比较器的输出保持不变。比较器通常有两个模拟电压输入端UIN+和UIN-以及一个数字状态输出端UOUT。当UIN+大于UIN-时,UOUT为高电平;当UIN+小于UIN-时,UOUT为低电平。根据具体的系统定义,这两种电位的具体数值可以有所不同。

LuDvei的博客 2万+

华为2288H-V5服务器做raid的详细步骤

操作步骤 服务器启动界面 按del键进入设置输入密码,2288H-v5默认密码是Admin@9000 移动光标选择device manager 驱动管理选择configurationutility 配置管理 选择main menu 主菜单 选择第一个 configuration management 配置管理 选择create virtual driver 创建虚拟驱动选择select RAID level 选择RAID等级选择level-5 选择select drivers 选择驱......

weixin_46041124的博客 1万+

【C++笔记】C++编译器拷贝优化和内存管理

哈喽,各位小伙伴大家好!上期我们讲了类和对象的收尾。但是还有一个拷贝的编译器优化没讲。今天我们就来讲一下C++拷贝的编译器优化和内存管理。话不多说,咱们进入正题!向大厂冲锋!

大白的编程日记. 2167

多模态大模型在AI人工智能领域的创新实践探索

随着人工智能技术的飞速发展,单一模态的数据处理已经难以满足复杂现实场景的需求。多模态大模型通过融合多种模态的数据,如文本、图像、音频等,能够更全面、深入地理解和处理信息,为人工智能的发展带来了新的机遇和挑战。本文的目的在于深入探讨多模态大模型在AI人工智能领域的创新实践,包括其核心概念、算法原理、实际应用等方面,旨在为相关领域的研究人员和从业者提供全面的技术参考和实践指导。本文的范围涵盖了多模态大模型的基本原理、算法实现、数学模型、项目实战以及实际应用场景等多个方面。

大模型应用工坊 1316

Linux内存uncache区域拷贝优化

1.概述 内存非cache区域拷贝速度很慢,严重影响了系统性能,因此采用多种方法进行优化,主要有对齐拷贝、批量拷贝、减少循环次数、NEON拷贝方法。 2.高级SIMD和浮点寄存器介绍 2.NEON指令 2.1 VLDR VLDR指令可从内存中将数据加载到扩展寄存器中。 VLDR{<c>}{<q>}{.64} <Dd>, [<Rn> {, #+/-<imm>}] Encoding T1/A1, immediate form VLDR{<c

非专业业余程序员 2882

RK3588开发笔记-USB3.0接口调试

RK3588平台是一款强大的处理器,但要充分发挥其性能,需要正确调试其各种接口,包括USB 3.0接口。USB 3.0接口提供了更快的数据传输速度,但也可能面临一些问题。本篇博客将介绍如何在RK3588平台上调试USB 3.0接口,以确保其稳定性和性能。RK3588芯片具有强大的USB资源,包括多个USB接口和高速数据传输能力。RK3588通常具有多个USB接口,包括USB 2.0和USB 3.0。USB 2.0用于低速和全速设备连接,而USB 3.0则用于高速设备连接。

flypig has a dream! 2560

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

C++内存拷贝优化总结

在C++开发中,频繁的内存拷贝操作会直接影响系统的整体性能,通过合理利用SIMD指令集、优化拷贝函数以及设计高效的内存池,开发者可以在多种应用场景中获得显著的性能提升。本文将从系统指令级优化、拷贝函数优化以及大内存分配优化等角度,详细探讨C++中的内存拷贝优化策略。

Where there is life, there is hope. 2794

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

实现一个高效率的内存拷贝函数memcpy

内存拷贝函数memcpy memcpy是memory copy的缩写,意为内存复制,在写C语言程序的时候,我们常常会用到它。它的函原型如下: void *memcpy(void *dest, const void *src, size_t n); 它的功能是从src的开始位置拷贝n个字节的数据到dest。如果dest存在数据,将会被覆盖。memcpy函数的返回值是dest的指针。memcpy函数定义在string.h头文件里。 自己实现的时候,最简单的方法是用指针按照字节顺序复制即可。但是性能太低,因为其

wyk71456的博客 9481

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C# 图像内存高速拷贝:使用Marshal.AllocHGlobal 与 ArrayPool 实现内存拷贝的几个方法

C# 图像内存高速拷贝:使用Marshal.AllocHGlobal 与 ArrayPool 实现内存拷贝的几个方法

xianzuzhicai的博客 1485

内存优化:从堆分配到零拷贝的终极重构

本文深入探讨了C++高性能开发中的内存优化技术。首先分析了堆内存分配的隐藏成本与内存碎片化问题,介绍了Valgrind等诊断工具的使用。重点讲解了内存池预分配技术的实现原理与线程安全设计,通过对象池示例展示了如何减少系统调用开销。在智能指针优化方面,对比了std::make_shared与传统构造方式的性能差异,解析了控制块内存布局。文章还涉及零拷贝数据传输与自定义分配器设计等进阶内容,为开发者提供了一套完整的内存优化方法论,帮助解决高频调用场景下的性能瓶颈问题。

技术分享 1739

[转]内存拷贝的优化方法

转自:http://flier.cnblogs.com/archive/2004/07/08/22352.html http://www.blogcn.com/user8/flier_lu/index.html?id=1577440&run=.0999083 让我们回过头来看看P4架构下的Cache结构。       The IA-32 Intel Architecture Softw

shines77 的博客 2492

内存拷贝效率优化

像memcpy是经常用到,但memcpy实际上并不是像我们知道那样一个字节一个字节的拷贝 memcpy是库函数,并不是C语言,有些场合下要自己写代码实现 1. 最简单实现方式 一个个字节的实现 void Mem_Cpy(void *pTag,const void *pSrc,int nLen) { unsigned char *pTagBuf = (unsigned char *...

小刚专栏 3480

ZLMediaKit内存优化:零拷贝技术与内存池设计原理

在流媒体服务器开发中,内存管理是性能优化的核心挑战之一。面对海量并发连接、实时音视频数据处理、低延迟传输等需求,传统的内存分配方式往往成为性能瓶颈。ZLMediaKit作为一款高性能运营级流媒体服务框架,通过创新的零拷贝(Zero-Copy)技术和高效内存池设计,实现了单机10W级别播放器支持和100Gb/s级别IO带宽能力。 本文将深入解析ZLMediaKit在内存优化方面的核心技术原理,帮助...

gitblog_01200的博客 679

比memcpy更好的内存拷贝方法

偶然间看到一个叫xmemcpy的工具,用做内存拷贝。号称在拷贝120字节以内时,比glibc提供的memcpy快10倍,并且有实验数据。这让人感觉很诧异。一直以来都觉得memcpy是很高效的。相比于strcpy等函数的逐字节拷贝,memcpy是按照机器字长逐字进行拷贝的,一个字等于4(32位机)或8(64位机)个字节。CPU存取一个字节和存取一个字一样,都是在一条指令、一个内存周期内完成的。显

fyl19870803的专栏 4185
上一篇: 远程控制编程中多窗口实现
下一篇: PcShare2005代码阅读(1)
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值