【问题标题】:Aligning both source and destination address in memcpy在 memcpy 中对齐源地址和目标地址
【发布时间】:2016-05-16 01:52:10
【问题描述】:

我想编写一个逐字复制而不是逐字节复制的 memcpy 代码以提高速度。 (虽然我需要为最后一个或几个字节做一些逐字节的复制)。所以我希望我的源地址和目标地址正确对齐。 我在glibc中看到了memcpy的实现 https://fossies.org/dox/glibc-2.22/string_2memcpy_8c_source.html 它仅对目标地址进行对齐。但是即使源地址没有正确对齐,它也会导致总线错误(考虑在我的 cpu 中启用了对齐检查)我不确定如何使源地址和目标地址正确对齐。因为如果我尝试通过逐字节复制几个字节来对齐源,它也会改变目标地址,所以起初正确对齐的目标地址现在可能无法正确对齐。 那么有什么办法可以同时对齐吗?请帮帮我。

void  memcpy(void  *dst,  void  *src,int  size)
{
   if(size >= 8)
   {
     while(size/8) /* code will give sigbus error if src = 0x10003 and dst = 0x100000 */ 
     {
       *((double*)dst)++  =  *((double*)src)++; 
        size  =  size  -  8;
     }
   }

   while(size--)
   {
     *((char*)dst)++  =  *((char*)src)++;
   }
}

【问题讨论】:

  • 我不明白你如何对齐源地址和目标?对不起,我就是不明白?
  • @MartinJames 考虑我正在传递 src = 0x10003 dest = 0x100000 和 15 的大小。如果在 memcpy 中我想逐字复制,我想将 src 对齐为 8 的倍数(64位操作系统)所以下一个数字是 8 的倍数是 0x10006,所以对于前 3 个字节,我将逐字节复制,如果我这样做,src 将是 0x10006 但 dest = 0x100003,现在 dest 不是 8 的倍数. 这个问题还会继续。那么如何在这里对齐 src 和 dest 。如果不可能,还有其他方法可以逐字复制吗?
  • 你不能。假设您正在移动一个单词块,如果源是奇数但目标是偶数,那么您在某种意义上是对齐未对齐的单词。另一方面,如果源是偶数而目标是奇数,则说明对齐的单词未对齐。您将读取或写入未对齐的单词 - 除了一次复制一个字节之外,别无他法。
  • @500-InternalServerError 所以如果我将 src 传递为奇数。根据这个fossies.org/dox/glibc-2.22/string_2memcpy_8c_source.html dest 检查内存对齐而不是源。所以代码总是会给出总线错误?
  • 源地址和目标地址必须错位相同的数量(可以为0) 该函数将有一些前导来确定错位是否相同,复制0或更多未对齐的字节,使用较大的移动指令复制移动的主体,然后是处理复制任何最终字节的拖车。在代码中的所有点,必须检查是否有更多要复制的内容。当源和目标不是相同的错位时,这种算法几乎无法获得。

标签: c alignment memcpy


【解决方案1】:

...所以起初正确对齐的目标地址现在可能无法正确对齐。那么有没有办法让两者对齐?

我在memcpy optimization 上找到了这篇文章,我相信这篇文章详细讨论了你正在尝试做的事情......

修改的 GNU 算法:

void * memcpy(void * dst, void const * src, size_t len)
{
    long * plDst = (long *) dst;
    long const * plSrc = (long const *) src;

    if (!(src & 0xFFFFFFFC) && !(dst & 0xFFFFFFFC))
    {
        while (len >= 4)
    {
            *plDst++ = *plSrc++;
            len -= 4;
        }
    }

    char * pcDst = (char *) plDst;
    char const * pcDst = (char const *) plSrc;

    while (len--)
    {
        *pcDst++ = *pcSrc++;
    }

    return (dst);
} 

【讨论】:

  • 谢谢你的回答,但是这里的代码首先检查目标和源是否都是4字节对齐的。如果它们对齐,它会进行长复制。否则逐字节复制。但是如果大小超过 1000,为了速度,直到某个时候,我会逐字节复制,直到我的 src 和 dest 都对齐。然后我会逐字复制,最后再逐字节地做剩下的。对于逐字复制,源和目标都需要单词对齐。我想要一种方法来做到这一点。
  • @Praveen:即使你采用贪婪的方法,也应该检查它是否是相同的错位,在这种情况下,你可以通过最初的 1/2/3 字节复制来纠正它。
【解决方案2】:

使用您包含的 glibc memcpy 代码,如果没有对齐内存,就无法调用该函数。如果您要自己编写,按照我的看法,memcpy 有两种可能的对齐方式:

1) 两个缓冲区都从四字节边界偏移了相同的量,或者两者都已经在四字节边界上。 (src % 4 == dst % 4) 在这种情况下,逐字节复制前几个字节,然后仅使用目标地址的对齐方式就可以了。

2) 缓冲区不在同一边界上。 (src % 4 != dst % 4) 在这种情况下,为了从一个对齐方式复制到另一个对齐方式,一次一个单词,处理器必须遵循类似于以下的过程:

Load the new word
Split it into an upper half and lower half. 
Shift the upper half down
Shift the lower half up
Add the upper half the previous lower half. 
Store the combined copy to memory
Repeat

我不确定这会比逐字节复制更快。如果您的处理器架构允许,并且两个缓冲区都在半字上对齐,那么半字半字可能会更快,尽管我在支持半字加载/存储的架构上看到的大多数 memcpy 实现已经这样做了。

【讨论】:

    猜你喜欢
    • 2021-06-21
    • 2010-12-16
    • 1970-01-01
    • 1970-01-01
    • 2014-08-20
    • 2018-05-02
    • 2012-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多