当前位置: 首页 > news >正文

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

封面出自:板栗懒得很

  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。有兴趣的同学可以研究一下。

相关文章:

  • DXOMark是如何评价音频质量的
  • 【每周一记-001】~~~
  • 【音视频连载-009】第二季 FFmpeg 打造简易播放器
  • 【每周一记-002】
  • iOS中使用OpenGL 实现增高功能
  • 五分钟用C++11实现Android系统的Handler机制
  • 从入门到进阶|如何基于WebRTC搭建一个视频会议
  • 【音视频连载-010】第二季 FFmpeg 日志打印
  • 如何将ijkplayer引入AS工程中进行二次开发
  • Android 性能优化必知必会
  • 音视频面试基础题
  • 2020年中国音频产业生态发展分析
  • 疫情下的网络视频行业观察 | 从疫情看网络视频领域竞争方向
  • 为什么小姐姐能摇一晚上不倒?
  • Android 热修复 AndFix 原理,看这篇就够了
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 「译」Node.js Streams 基础
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【Leetcode】104. 二叉树的最大深度
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Docker 笔记(2):Dockerfile
  • es6要点
  • git 常用命令
  • java 多线程基础, 我觉得还是有必要看看的
  • magento2项目上线注意事项
  • Node 版本管理
  • Redis字符串类型内部编码剖析
  • Spring Cloud Feign的两种使用姿势
  • 产品三维模型在线预览
  • 解决iview多表头动态更改列元素发生的错误
  • 前端性能优化——回流与重绘
  • 怎么把视频里的音乐提取出来
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​​​​​​​​​​​​​​Γ函数
  • ​ssh免密码登录设置及问题总结
  • ​一些不规范的GTID使用场景
  • #Java第九次作业--输入输出流和文件操作
  • #QT(智能家居界面-界面切换)
  • #图像处理
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • $.each()与$(selector).each()
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (转)c++ std::pair 与 std::make
  • (转)Linux整合apache和tomcat构建Web服务器
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .Net下的签名与混淆