全面优化你的memcpy!

http://blog.21ic.com/user1/2092/archives/2006/8095.html


 在BlackFin上面关全局中断其实是很麻烦的事.它不是清除或者设置一个全局中断使能位,而是把core中断屏蔽寄存器所有位都清成0,这个时候你需要设置一个变量来保存以前在core中断屏蔽寄存器中的值,以便后面恢复,这里我给出前段时间写的两个函数。由于BlackFin的特殊硬件结构,只要你想,可以优化一下它,使用能并行执行的指令。


static inline void disable_interrupt(void *ptr)
{
    asm("P0 = R0;");
    asm("CLI R0;");
    asm("[P0] = R0;");
    
}


static inline void enable_interrupt(void *ptr)
{
    asm("P0 = R0;");
    asm("R0 = [P0];");
    asm("STI R0;");
    
}


    另外,在系统设计中,我们经常会用到memcpy函数,尤其是在管理循环缓存时(这时可能就没有DMA能给你使用了,呵呵!)。移动的数据量很大,这时高效率的memcpy就很是重要了。对于SDRAM-->SDRAM的数据搬移,当需要memcopy函数传输超过35个字的时候,先把SDRAM中的数据搬到L1中暂存,然后搬到目的SDRAM中,这样效率会更高,这里我提供了一个在C语言级别优化memcpy函数的例子:

void memcopy(void *dest, const void *src, int size)
{
    #define L1_BUFF 2048
    long temp_array[L1_BUFF/sizeof(long)];
    
    while(size/L1_BUFF){
        memcpy(temp_array, src, L1_BUFF);
        memcpy(dest, temp_array,L1_BUFF);
        src = (void *)((char *)src + L1_BUFF);
        dest = (void *)((char *)dest + L1_BUFF);
        size -= L1_BUFF;
    }
    
    memcpy(temp_array, src, size);
    memcpy(dest, temp_array,size);
    
    
}
    熟悉C的都知道,定义的局部变量数组是在堆栈里面分配内存的,另外为了函数调用和中断的快速上下文切换的快速效率着想,堆栈一般是放在CPU内部的sram里面的,访问速度是core clk,所以temp_array就是在sram里面分配内存的。在使用循环缓存的管理函数的系统中,调用memcopy函数的函数一般都是比较靠顶层的函数,所以你使用了这个函数也不会对你构建的系统的堆栈空间提出更多的要求。(例如我前段日子做的MP4播放器,就几乎没有影响到原来的堆栈空间的size)。下面我来说说这段程序的设计原理:
有BF533硬件参考书的人可以翻到7-11的表,说到:16-bit SDRAM——>L1 Data memory的搬移n字的数据需要n+14个系统时钟周期,而反过来搬移n个字需要n+11个系统时钟周期,SDRAM——>SDRAM n字数据搬移需要10+(17n/7)个系统时钟周期。看到这里大家可以发现,SDRAM到SDRAM的n个字的数据搬移比L1到SDRAM和到L1的数据搬移同样n个字所花的系统时钟周期的总和还多,这样我们可以很方便的建立一个不等式:
    (17/7)n+10 > 2n+25
得n > 35时,对于SDRAM至SDRAM的数据搬移,改成先SDRAM至L1,然后L1至SDRAM的搬移效率会提高一些。这是理论值,实际的有些偏差。
    为什么会出现这种情况呢?感兴趣的可以去查看SDRAM的原理中的突发模式,这里我就不多说了。下面给出我的测试数据:
CCLK=594MHz,SCLK=118.8MHz

在传输10000字节时memcpy函数花费的时间是memcopy的1.92倍;
1000字节时为1.12倍,传输10000字节时更是在2.28倍之多。
可见熟悉了硬件原理,优化真的就是无止尽!
后记:上面测试的memcopy函数与系统提供的memcpy函数的性能比值是在堆栈分配在数据L1里面的时候得到的。前些天,我把堆栈放在scratch sram里重新测试了一下,在传输大于1000字节的数据时,memcopy的性能是memcpy的约1.6倍:在1.59至1.63被之间。

并给出Linux下嵌入汇编的函数设计风格的开关全局中断函数:
static inline void disable_interrupt(void *ptr)
{
    __asm__ __volatile__
    (
    "P0 = %0;"
    "cli %0;"
         "[P0] = %0;"
    
    :
    : "r" (ptr)
    
    );

}



static inline void enable_interrupt(void *ptr)
{
    __asm__ __volatile__
    (
    "P0 = %0;"
    "%0 = [P0];"
         "STI %0;"
    
    :
    : "r" (ptr)
    
    );

}

效率会底一点。

----------------------------------------------------------------

http://bbs.unixbeta.com/archiver/tid-50260.html

使用库函数的memcpy会有优化,如果count是4的倍数,会直接使用rep movs指令做复制,即便不是4的倍数,也会先用rep movs指令复制,不足部分再用字或字节复制补齐。因为复制是4字节一个单位,所以可以效率提高很多。
而rep movs必定是从低到高的,不过memcpy对高到低还是低到高没有绝对的规定,如果是memmove,他会根据实际情况来选择是从低到高复制还是相反,因为复制源和目标可能会有重叠部分,这时候顺序就很重要了,使用memmove总是可以安全的进行复制。
VC的memcpy与memmove基本相同,有重叠部分也可以做到安全复制。


对不同的cpu,应该优化的方法不一样,比如有些支持多字节传送指令的
但最起码的一点,就是要字拷贝


优化:比如可以转换为int等机器字类型的指针来复制可以显著提高效率


优化memcpy一定要站在CPU的角度。比如一次复制寄存器宽度的数据。
什么assert啊都没有必要。可以看看glibc中的相关汇编代码。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中,可以使用以下几种方法来优化`memcpy`函数的性能: 1. 使用优化的编译选项:确保编译器使用了适当的优化选项。比如,使用`-O2`或`-O3`选项进行编译,这些选项会启用更多的优化。 2. 使用平台特定的实现:许多平台都提供了针对特定硬件架构优化的`memcpy`实现。查阅相关文档或平台提供的库函数来获取更高效的实现。 3. 使用SIMD指令:SIMD(Single Instruction Multiple Data)指令集可以对多个数据元素同时执行操作,提高数据处理速度。使用适当的SIMD指令集如SSE(Intel)或NEON(ARM)可以加速`memcpy`函数。可以使用相关的库函数,如`memcpy_sse`或`memcpy_neon`。 4. 使用循环展开:循环展开是一种优化技术,将循环体内的代码复制多次,减少循环迭代的次数。这样可以减少循环的开销和分支预测失败的可能性。但是,循环展开可能会增加代码大小和缓存压力。可以根据情况手动展开循环或使用编译器提供的选项来实现自动展开。 5. 使用异步操作:如果目标平台支持异步操作,可以使用多线程或异步IO来并行执行`memcpy`操作,以提高性能。 6. 使用定向内存访问:尽量使用与内存对齐对应的数据类型进行访问,以避免未对齐访问的性能损失。 这些是一些常见的优化方法,具体的优化策略可能因平台和需求而异。在进行优化时,建议结合具体场景和测试来评估性能,并根据实际情况选择最适合的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值