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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从一个内存复制到另一个跳过C中的常量字节相关的知识,希望对你有一定的参考价值。

我正在研究嵌入式系统应用程序。我想从源复制到目标,跳过常量字节数。例如:source[6] = {0,1,2,3,4,5},我希望目标为{0,2,4}跳过一个字节。不幸的是memcpy无法满足我的要求。如何在不使用循环的情况下在'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; 
}
答案

你可以编写一个“樱桃选择器”功能

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

最多复制num“物体”,每个物体的大小sizesourcedestination。仅复制满足测试的对象。然后用

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);

.

另一答案

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

此外,通常memcpy()优化复制较大的作品。例如,如果你复制一个字节数组但CPU能够一次复制32位(4字节),那么memcpy()可能会将大部分复制作为一个循环,每次迭代4个字节(减少数量)读取和写入,减少循环迭代次数)。

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

为了避免这种情况,你真的想要将需要复制的数据与不能复制的数据分开 - 例如把所有不需要复制的东西放在数组的末尾,只复制数组的第一部分(这样它就可以“复制一个连续的字节区域”)。

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

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

另一答案

忽略了你的速度要求:你可能会试图找到一种解决问题而不复制的方法。

这里有一些想法:

如果你想迭代destination数组你可以为source定义一种“挑剔的迭代器”,它会前进到你允许的下一个数字:而不是iter++iter = advance_source(iter)

如果你想搜索destination数组,那么在bsearch()周围包裹一个函数,搜索source并检查结果。等等。

另一答案

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

您需要知道您的处理器是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;
}

以上是关于从一个内存复制到另一个跳过C中的常量字节的主要内容,如果未能解决你的问题,请参考以下文章

14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段

c语言中字符型数据在内存中的存储形式是?

如何将数据从一个活动传递到android中的另一个活动片段? [复制]

如何将列表视图中的数据从一个片段发送到另一个片段

在C中将奇数索引元素从一个数组复制到另一个数组

Android:如何在选项卡内从一个片段导航到另一个片段? [关闭]