从一个内存复制到另一个跳过C中的常量字节

时间:2018-03-05 17:29:36

标签: c memory

我正在研究嵌入式系统应用程序。我想从源复制到目标,跳过常量字节数。例如:source[6] = {0,1,2,3,4,5}我希望目标为{0,2,4}跳过一个字节。遗憾的是memcpy无法满足我的要求。我怎样才能在C' C'不使用循环,因为我有大量数据要处理和使用循环体验时间开销。

我目前的实现是这样的,需要5-6毫秒才能复制1500字节:

unsigned int len_actual = 1500; 
/* Fill in the SPI DMA buffer. */
 while (len_actual-- != 0) 
{
*(tgt_handle->spi_tx_buff ++) = ((*write_irp->buffer ++)) | (2 << 16) | DSPI_PUSHR_CONT; 
}

4 个答案:

答案 0 :(得分:0)

你可以写一个&#34;樱桃选择器&#34;功能

void * memcpk(void * destination, const void * source, 
              size_t num, size_t size
              int (*test)(const void * item));

最多复制num&#34;对象&#34;,每个对象的大小为size sourcedestination。仅复制满足测试的对象。 然后用

int oddp(const void * intptr) { return (*((int *)intptr))%2; }
int evenp(const void * intptr) { return !oddp(intptr); }

你可以做到

 int destination[6];
 memcpk(destination, source, 6, sizeof(int), evenp);

答案 1 :(得分:0)

几乎所有的CPU都有缓存;这意味着(例如)当您修改一个字节时,CPU从RAM中获取整个缓存行,修改缓存中的字节,然后将整个缓存行写回RAM。通过跳过小块,可以增加开销(更多关于CPU的说明)并且不会减少缓存和RAM之间传输的数据量。

此外,通常memcpy()已经过优化,可以复制较大的作品。例如,如果复制一个字节数组但CPU能够一次复制32位(4个字节),则memcpy()可能会将大部分复制作为每次迭代4个字节的循环进行(减少读写次数,减少循环迭代次数。)

换句话说;由于多种原因,避免复制特定字节的代码会使其明显慢于mempcy()

为避免这种情况,您确实希望将需要复制的数据与不包含的数据分开 - 例如把所有不需要复制的东西放在数组的末尾,只复制数组的第一部分(这样它就会保留&#34;复制一个连续的字节区域&#34;)。

如果你不能这样做,下一个考虑的选择就是掩盖。例如,如果你有一个字节数组,其中某些字节不应被复制,那么你也有一个&#34;掩码字节数组&#34;并在循环中执行dest[i] = (dest[i] & mask[i]) | (src[i] & ~mask[i]);之类的操作。这听起来很可怕(而且很可怕),直到你通过操作更大的碎片来优化它 - 例如如果CPU可以复制32位片段,则屏蔽允许您通过假装所有阵列都是uint32_t的数组来每次迭代完成4个字节。请注意,对于这种技术,更宽更好 - 例如如果CPU支持对256位片段(80x86上的AVX)的操作,则每次循环迭代可以执行32个字节。如果您可以保证大小和对齐,也会有所帮助(例如,如果CPU一次可以运行32位/ 4字节,请确保数组的大小始终是4个字节的倍数,并且数组始终是4字节对齐;即使它意味着在末尾添加未使用的填充。)

另请注意,根据实际的CPU,可能会在指令集中提供特殊支持。例如,现代80x86 CPU(支持SSE2)具有maskmovdqu指令,专门用于有选择地写入一些字节而不是其他字节。在这种情况下,你需要诉诸于内在的或内联组装,因为&#34;纯粹的C&#34;不支持这种类型的东西(除了按位运算符)。

答案 2 :(得分:0)

忽略了你的速度要求: 您可以尝试找到一种解决问题而无需复制的方法。

这里有一些想法:

如果要迭代可以定义的destination数组 一种&#34;挑剔的迭代器&#34; source的{​​{1}}前进到您允许的下一个数字:而不是iter++执行iter = advance_source(iter)

如果要搜索destination数组,请在搜索bsearch()的{​​{1}}周围打一个函数并检查结果。等等。

答案 3 :(得分:0)

根据您的处理器内存宽度和内部寄存器的数量,您可以通过使用移位操作来加快速度。

您需要知道您的处理器是big-endian还是little-endian。

假设您有一个32位处理器和总线,以及至少4个备用寄存器,编译器可以使用这些寄存器进行优化。这意味着您可以在同一个目标字中读取或写入4个字节,读取2个源字。请注意,您正在读取要丢弃的字节。

您还可以通过确保所有内容都是字对齐来提高速度,并忽略缓冲区之间的间隙,因此不必担心奇数字节数。

所以,对于小端:

inline unsigned long CopyEven(unsigned long a, unsigned long b)
{
  long c = a & 0xff;
  c |= (a>>8) & 0xff00;
  c |= (b<<16) & 0xff0000;
  c |= (b<<8) &0xff000000;
  return c;
}

unsigned long* d = (unsigned long*)dest;
unsigned long* s = (unsigned long*)source;
for (int count =0; count <sourceLenBytes; count+=8)
{
   *d = CopyEven(s[0], s[1]);
   d++;
   s+=2;
}