最终的 ARM Linux 内存碎片与 NEON Copy 但不是 memcpy

Posted

技术标签:

【中文标题】最终的 ARM Linux 内存碎片与 NEON Copy 但不是 memcpy【英文标题】:Eventual ARM Linux Memory Fragmentation with NEON Copy but not memcpy 【发布时间】:2018-04-11 16:48:43 【问题描述】:

我在 BeagleBone X-15 (ARM Cortex-A15) 板上运行 Linux 4.4。我的应用程序映射 SGX GPU 的输出,需要复制 DRM 后备存储。

memcpy 和我的自定义 NEON 复制代码都可以工作...但是 NEON 代码要快得多(~11ms vs. ~35ms)。

我注意到,相当一致地,在 12500 秒后,当我使用 NEON 版本的副本时,Linux 会因内存不足 (OOM) 而终止应用程序。当我运行应用程序并将一行从 NEON 副本更改为标准 memcpy 时,它会无限期地运行(到目前为止 12 小时......)。但是复制比较慢。

我在下面粘贴了 mmap、copy 和 NEON 复制代码。我的 NEON 副本真的有问题吗?谢谢。

霓虹灯复制:

/**
* CompOpenGL neonCopyRGBAtoRGBA()
* Purpose: neonCopyRGBAtoRGBA - Software NEON copy
*
* @param src - Source buffer
* @param dst - Destination buffer
* @param numpix - Number of pixels to convert
*/
__attribute__((noinline)) void CompOpenGL::neonCopyRGBAtoRGBA(unsigned char* src, unsigned char* dst, int numPix)


    (void)src;
    (void)dst;
    (void)numPix;

    // This case takes RGBA -> BGRA
    __asm__ volatile(
                "mov r3, r3, lsr #3\n"           /* Divide number of pixels by 8 because we process them 8 at a time */
                "loopRGBACopy:\n"
                "vld4.8 d0-d3, [r1]!\n"        /* Load 8 pixels into d0 through d2. d0 = R[0-7], d1 = G[0-7], d2 = B[0-7], d3 = A[0-7] */
                "subs r3, r3, #1\n"              /* Decrement the loop counter */
                "vst4.8 d0-d3, [r2]!\n"        /* Store the RGBA into destination 8 pixels at a time */
                "bgt loopRGBACopy\n"
                "bx lr\n"
                );


在这里Mmap和复制代码:

union gbm_bo_handle handleUnion = gbm_bo_get_handle(m_Fb->bo);
struct drm_omap_gem_info gemInfo;
char *gpuMmapFrame = NULL;
gemInfo.handle = handleUnion.s32;
int ret = drmCommandWriteRead(m_DRMController->m_Fd, DRM_OMAP_GEM_INFO,&gemInfo, sizeof(gemInfo));
if (ret) 
    qDebug() << "Cannot set write/read";

else 
    // Mmap the frame
    gpuMmapFrame = (char *)mmap(0, gemInfo.size, PROT_READ | PROT_WRITE, MAP_SHARED,m_DRMController->m_Fd, gemInfo.offset);

    if ( gpuMmapFrame != MAP_FAILED ) 

        QElapsedTimer timer;
        timer.restart();

        //m_OGLController->neonCopyRGBAtoRGBA((uchar*)gpuMmapFrame,  (uchar*)m_cpyFrame,dmaBuf.width * dmaBuf.height);
        memcpy(m_cpyFrame,gpuMmapFrame,dmaBuf.height * dmaBuf.width * 4);

        qDebug() << "Copy Performance: " << timer.elapsed();

【问题讨论】:

寄存器从'R0'传递过来;我猜你有一个 C++ 的“this”。您的霓虹灯组装器可能是错误的。使用输入/输出说明符来告诉编译器你在做什么,并做这个错误证明,不需要 (void) casts 和 'noinline'。不知道编译试图做什么(给定您设置的编译选项),因为您的汇编代码没有提示它正在发生什么。此外,您的“bx lr”将否定任何可能溢出堆栈的编译尾声。尝试让它与输入/输出参数一起编译,你会避免沮丧。 memcpy() 相比,NEON 代码不会导致碎片化的直接影响。这是汇编器和编译器之间的某种接口错误。使用 neonCopyRGBAtoRGBA 尝试 objdump -S 以查看编译是否围绕您的内联汇编程序进行。 【参考方案1】:

好消息是,如果您将 vld4/vst4 替换为 vld1/vst1,您的函数将运行得更快。

坏消息是您必须报告您使用和修改了哪些寄存器,包括CPSR 和内存,并且您不应该从内联汇编返回。 (bx lr)。

__asm__ volatile(
                "mov r3, r3, lsr #3\n"           /* Divide number of pixels by 8 because we process them 8 at a time */
                "loopRGBACopy:\n"
                "vld1.8 d0-d3, [r1]!\n"        /* Load 8 pixels into d0 through d2. d0 = R[0-7], d1 = G[0-7], d2 = B[0-7], d3 = A[0-7] */
                "subs r3, r3, #1\n"              /* Decrement the loop counter */
                "vst1.8 d0-d3, [r2]!\n"        /* Store the RGBA into destination 8 pixels at a time */
                "bgt loopRGBACopy\n"
                ::: "r1", "r2", "r3", "d0", "d1", "d2", "d3", "cc", "memory"
                );

http://www.ethernut.de/en/documents/arm-inline-asm.html

【讨论】:

以上是关于最终的 ARM Linux 内存碎片与 NEON Copy 但不是 memcpy的主要内容,如果未能解决你的问题,请参考以下文章

ARM NEON:由于内存访问带宽有限而预测性能问题的工具?

原创Linux内存管理 - zoned page frame allocator - 4

ARM NEON:从 NEON 寄存器(Q/D 寄存器)中包含的地址加载数据

linuxARM板子开启浮点和neon加速

与 ARM Neon vtbx 的字节顺序混淆

使用NEON优化ARM的卷积运算