30

memcpy 速度太慢?掌握这个技术让内存拷贝效率成倍提升

 4 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzA4MjU1MDk3Ng%3D%3D&%3Bmid=2451526913&%3Bidx=1&%3Bsn=f9c647604c97f116eaf7fac6e6af0b7e&%3Bchksm=886ffcaebf1875b84bc7fc6edbab3962230ecd919f915d3fe11c56f2591eb3fc213c6ebc446b&%3Btoken=2044115023&%3Blang=zh_CN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

封面出自:板栗懒得很

memcpy 是C/C++的一个标准函数,原型 void *memcpy(void *dest, const void *src, size_t n) ,用于从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

neon 是适用于ARM Cortex-A系列处理器的一种128位SIMD(Single Instruction, Multiple Data,单指令、多数据)扩展结构。neon支持一次指令处理多个数据,比如处理8个8-bit、4个16-bit、2个32-bit或1个64-bit。正是这个特性可以用于加速内存拷贝。

  在正常情况下 memcpy

的性能已经足够使用了,但是当我们因为某些原因在拷贝大内存遇到瓶颈的时候,可以考虑使用neon来加速内存拷贝。比如我在使用glMapBufferRange把PBO从GPU内存映射到CPU内存的时候遇到了耗时问题,拷贝921600字节的数据需要30ms,在使用neon后,内存拷贝耗时直接降低到了4ms,相差将近8倍。事实上,在arm平台上使用neon指令可以高效提升数据并行处理性能,而不仅仅局限于内存拷贝。google开源的

libyuv

内部也使用了neon指令来并行处理数据。

使用neon指令

#ifdef __ARM__

static void neon_memcpy(volatile unsigned char *dst, volatile unsigned char *src, int sz)

{

if (sz & 63)

sz = (sz & -64) + 64;

asm volatile (

"NEONCopyPLD: \n"

" VLDM %[src]!,{d0-d7} \n"

" VSTM %[dst]!,{d0-d7} \n"

" SUBS %[sz],%[sz],#0x40 \n"

" BGT NEONCopyPLD \n"

: [dst]"+r"(dst), [src]"+r"(src), [sz]"+r"(sz) : : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc", "memory");

}

#endif

  由于并不是所有的armv7架构cpu都支持neon,所以这里增加cpufeatures库用来判断是否支持neon。下面是正确的使用方式。

#ifdef __ARM__

if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&

(android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0){//支持NEON

neon_memcpy(destBuffer, src, length);

}else{

memcpy(destBuffer, src, length);

}

#else

//其它架构使用memcpy

memcpy(destBuffer, src, length);

#endif

Android mk开启neon

#arm架构增加neon支持

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)

LOCAL_CFLAGS := -D__cpusplus -g -mfloat-abi=softfp -mfpu=neon -march=armv7-a -mtune=cortex-a8 -DHAVE_NEON=1

endif

#开启两个架构的neon支持(x86可以通过将neon转为sse间接支持)

ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI), armeabi-v7a x86))

LOCAL_ARM_NEON := true

endif

LOCAL_STATIC_LIBRARIES := cpufeatures


include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/cpufeatures)

Cmake开启neon

# 引入cpufeatures模块

include_directories(${ANDROID_NDK}/sources/android/cpufeatures)


if (${ANDROID_ABI} STREQUAL "armeabi-v7a")

set_property(SOURCE ${SOURCES} APPEND_STRING PROPERTY COMPILE_FLAGS " -mfpu=neon")

add_definitions("-DHAVE_NEON=1")

elseif (${ANDROID_ABI} STREQUAL "x86")

set_property(SOURCE ${SOURCES} APPEND_STRING PROPERTY COMPILE_FLAGS

" -mssse3 -Wno-unknown-attributes \

-Wno-deprecated-declarations \

-Wno-constant-conversion \

-Wno-static-in-inline")

add_definitions(-DHAVE_NEON_X86=1 -DHAVE_NEON=1)

endif ()

add_library(

yourLibrary

SHARED

${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c

)

  事实上并不是只有arm架构才支持SIMD,x86也是支持的(SSE),并且Android也提供了适用于x86的NEON_2_SSE.h。x86并不直接支持neon指令,而是通过这个头文件将其转为sse指令,以提供与neon相同的api。有兴趣的同学可以研究一下。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK