从一个内存复制到另一个跳过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
“物体”,每个物体的大小size
从source
到destination
。仅复制满足测试的对象。然后用
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.类加载机制概(代码片段