因memcpy导致tda4vm上的h264解码占CPU较高而改弃,从网上找到各种memcpy的优化代码,在一起做了个运行速度对比,请查收;
#include <stdio.h>
#include <stdlib.h> /* rand, srand */
#include <string.h>
#include <assert.h>
#include <sys/time.h>
#include <time.h> /* time() */
/*
* 在uncache区域memcpy时通常很慢,下面是一些优化:
*/
/* arm下的memcpy实现: */
#if 0
void memcpy_neon(volatile void *dst, volatile void *src, int sz)
{
if (sz & 63) {
sz = (sz & -64) + 64;
}
asm volatile (
"NEONCopy: \n"
" VLDM %[src]!,{d0-d7} \n"
" VSTM %[dst]!,{d0-d7} \n"
" SUBS %[sz],%[sz],#0x40 \n"
" BGT NEONCopy \n"
: [dst]"+r"(dst), [src]"+r"(src), [sz]"+r"(sz) : : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc", "memory");
}
#endif
/*
* arm64
*/
/* uncached 区域: */
void memcpy_uncached(volatile void *dst, volatile void *src, int sz)
{
if (sz & 63) {
sz = (sz & -64) + 64;
}
asm volatile (
"sub %[dst], %[dst], #64 \n"
"1: \n"
"ldnp q0, q1, [%[src]] \n"
"ldnp q2, q3, [%[src], #32] \n"
"add %[dst], %[dst], #64 \n"
"subs %[sz], %[sz], #64 \n"
"add %[src], %[src], #64 \n"
"stnp q0, q1, [%[dst]] \n"
"stnp q2, q3, [%[dst], #32] \n"
"b.gt 1b \n"
: [dst]"+r"(dst), [src]"+r"(src), [sz]"+r"(sz) : : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc", "memory");
}
/* cached 区域: */
void memcpy_cached(volatile void *dst, volatile void *src, int sz)
{
if (sz & 63) {
sz = (sz & -64) + 64;
}
asm volatile (
"sub %[src], %[src], #32 \n"
"sub %[dst], %[dst], #32 \n"
"1: \n"
"ldp q0, q1, [%[src], #32] \n"
"ldp q2, q3, [%[src], #64]! \n"
"subs %[sz], %[sz], #64 \n"
"stp q0, q1, [%[dst], #32] \n"
"stp q2, q3, [%[dst], #64]! \n"
"b.gt 1b \n"
: [dst]"+r"(dst), [src]"+r"(src), [sz]"+r"(sz) : : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc", "memory");
}
static void get_rand_bytes(unsigned char *data, int len)
{
int i;
srand((unsigned)time(NULL)); //种下随机种子
for (i = 0; i < len; i++) {
data[i] = rand() % 255; //取随机数,并保证数在0-255之间
//printf("%02X ", data[i]);
}
}
static int get_cur_time_us(void)
{
struct timeval tv;
gettimeofday(&tv, NULL); //使用gettimeofday获取当前系统时间
return (tv.tv_sec * 1000 * 1000 + tv.tv_usec); //利用struct timeval结构体将时间转换为ms
}
#define ARRAY_SIZE(n) sizeof(n) / sizeof(n[0])
int main(void)
{
int size_list[] = {
1024 * 1024 * 10, // 10MB
1024 * 1024 * 1, // 1MB
1024 * 100, // 100KB
1024 * 10, // 10KB
1024 * 1, // 1KB
};
char *data1;
char *data2;
int t1;
int t2;
int i = 0;
data1 = (char *)malloc(size_list[0]);
data2 = (char *)malloc(size_list[0]);
get_rand_bytes((unsigned char *)data1, size_list[0]);
for (i = 0; i < ARRAY_SIZE(size_list); i++) {
t1 = get_cur_time_us();
memcpy(data2, data1, size_list[i]);
t2 = get_cur_time_us();
printf("copy %d bytes, memcpy waste time %dus\n", size_list[i], t2 - t1);
t1 = get_cur_time_us();
memcpy_uncached(data2, data1, size_list[i]);
t2 = get_cur_time_us();
printf("copy %d bytes, memcpy_uncached waste time %dus\n", size_list[i], t2 - t1);
t1 = get_cur_time_us();
memcpy_cached(data2, data1, size_list[i]);
t2 = get_cur_time_us();
printf("copy %d bytes, memcpy_cached waste time %dus\n\n", size_list[i], t2 - t1);
}
free(data1);
free(data2);
return 0;
}
#if 0
通用的memcpy优化方向:
1. 最大限度使用memory/cache带宽(Vector指令、指令级并行)
2. Load/Store地址对齐
3. 集中顺序访问
4. 适当使用non-temporal访存执令
5. 适当使用String指令来加速较大的拷贝
最后,所有的指令都经过CPU的流水线执行,因此对流水线效率的分析至关重要,需要优化指令顺序以避免造成流水线阻塞。
memcpy 和 memmove 函数作用是一样的,
唯一的区别是,当内存发生局部重叠的时候,memmove 保证拷贝的结果是正确的,memcpy 不保证拷贝的结果的正确。
只要没有内存重叠,memcpy()和memmove()的性能应相似。
#endif
#if 0
copy 10485760 bytes, memcpy waste time 6853us
copy 10485760 bytes, memcpy_uncached waste time 4953us
copy 10485760 bytes, memcpy_cached waste time 5013us
copy 1048576 bytes, memcpy waste time 497us
copy 1048576 bytes, memcpy_uncached waste time 403us
copy 1048576 bytes, memcpy_cached waste time 411us
copy 102400 bytes, memcpy waste time 39us
copy 102400 bytes, memcpy_uncached waste time 13us
copy 102400 bytes, memcpy_cached waste time 13us
copy 10240 bytes, memcpy waste time 2us
copy 10240 bytes, memcpy_uncached waste time 1us
copy 10240 bytes, memcpy_cached waste time 0us
copy 1024 bytes, memcpy waste time 1us
copy 1024 bytes, memcpy_uncached waste time 0us
copy 1024 bytes, memcpy_cached waste time 0us
#endif