This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
您好!
我 已经创建了一个 openvx 内核 ,它将一个 vx_tensor 复制到另一个。 每个内核都指定为内核的目标、唯一的区别是 A72使用 memcpy、C7X、C66、R5F 使用 uDMA。
下面是我复制一个大小为864*128*sizeof(float)=442368字节的 vx_tensor 得到的数字
内核 | memcpy |
appUdmaCopy1D
|
a72. | 670 μ s | 不适用 |
C66 | 4889 us | 2237 us |
C7X 内核上运行 | 1616 μ s | 2003 US |
R5F | 18097 us | 5794 us |
我得到的数字非常令人失望、UDMA 似乎比做 A72 memcpy 慢。 在 C7x 上、执行 mempcy 时甚至会更慢。
这些数字是否正常? 我没有找到 uDMA 的任何规格。
以下是我的(简化的)内核代码:
bool TensorBlockCpyDma(const size_t nBytes, const tivx_obj_desc_tensor_t* const srcDescriptor, const tivx_obj_desc_tensor_t* const dstDescriptor) { bool ret = false; if ( (0U == nBytes) || (NULL == srcDescriptor) || (NULL == dstDescriptor)) { VX_PRINT(VX_ZONE_ERROR, "Invalid input pointer\n"); } else { uint64_t srcPhys = tivxMemShared2PhysPtr(srcDescriptor->mem_ptr.shared_ptr, VX_MEMORY_TYPE_HOST); uint64_t dstPhys = tivxMemShared2PhysPtr(dstDescriptor->mem_ptr.shared_ptr, VX_MEMORY_TYPE_HOST); app_udma_copy_1d_prms_t prms; appUdmaCopy1DPrms_Init(&prms); prms.dest_addr = dstPhys; prms.src_addr = srcPhys; prms.length = nBytes; if (0 == appUdmaCopy1D(NULL, &prms)) { ret = true; } } return ret; } static vx_status VX_CALLBACK tivxTensorcpyProcess( tivx_target_kernel_instance kernel, tivx_obj_desc_t *obj_desc[], uint16_t num_params, void *priv_arg) { vx_status status = (vx_status)VX_SUCCESS; const tivx_obj_desc_tensor_t *src_desc; const tivx_obj_desc_tensor_t *dst_desc; if ( (num_params != TIVX_KERNEL_TENSORCPY_MAX_PARAMS) || (NULL == obj_desc[TIVX_KERNEL_TENSORCPY_SRC_IDX]) || (NULL == obj_desc[TIVX_KERNEL_TENSORCPY_DST_IDX]) ) { status = (vx_status)VX_FAILURE; } if((vx_status)VX_SUCCESS == status) { src_desc = (const tivx_obj_desc_tensor_t *)obj_desc[TIVX_KERNEL_TENSORCPY_SRC_IDX]; dst_desc = (const tivx_obj_desc_tensor_t *)obj_desc[TIVX_KERNEL_TENSORCPY_DST_IDX]; } if((vx_status)VX_SUCCESS == status) { void *src_target_ptr; void *dst_target_ptr; src_target_ptr = tivxMemShared2TargetPtr(&src_desc->mem_ptr); tivxCheckStatus(&status, tivxMemBufferMap(src_target_ptr, src_desc->mem_size, (vx_enum)VX_MEMORY_TYPE_HOST, (vx_enum)VX_READ_ONLY)); dst_target_ptr = tivxMemShared2TargetPtr(&dst_desc->mem_ptr); tivxCheckStatus(&status, tivxMemBufferMap(dst_target_ptr, dst_desc->mem_size, (vx_enum)VX_MEMORY_TYPE_HOST, (vx_enum)VX_WRITE_ONLY)); { /* call kernel processing function */ uint32_t start = tivxPlatformGetTimeInUsecs(); #ifdef A72 memcpy(dst_target_ptr, src_target_ptr, src_desc->mem_size); #else if (!TensorBlockCpyDma(src_desc->mem_size, src_desc, dst_desc)) { VX_PRINT(VX_ZONE_ERROR, "TensorBlockCpyDma failed\n"); } #endif uint32_t delta = tivxPlatformGetTimeInUsecs() - start; VX_PRINT(VX_ZONE_WARNING, "TensorBlockCpyDma copied %u bytes in %u us\n", src_desc->mem_size, delta); /* kernel processing function complete */ } tivxCheckStatus(&status, tivxMemBufferUnmap(src_target_ptr, src_desc->mem_size, (vx_enum)VX_MEMORY_TYPE_HOST, (vx_enum)VX_READ_ONLY)); tivxCheckStatus(&status, tivxMemBufferUnmap(dst_target_ptr, dst_desc->mem_size, (vx_enum)VX_MEMORY_TYPE_HOST, (vx_enum)VX_WRITE_ONLY)); } return status; }
A72内核调用 memcpy()而不是 appUdmaCopy1D()。
谢谢!
弗雷德
尊敬的 Fred:
A72 memcpy 优于 udma 副本、因为副本的大小很小。 只是450KB、可能 完全存储在缓存中、并且可以提供比 UDMA 更好的性能。 您能否尝试复制2MB 以上的内存? 我认为在本例中、uDMA 的性能将优于 A72上的 memcpy。
在 C7x 上、能否尝试使用 DRU 通道而不是 uDMA 通道?
此致、
Brijesh
感谢您的回答、Brijesh。
我尝试的 TensorCpy 是原始大小的5倍(2211840字节)、但 A72 memcpy 的性能仍然远远超过 DMA。
内核 | 864 * 128 * 5 * sizeof (float) |
a72 memcpy | 2033 us |
C66 DMA | 9460us |
C7X DMA (DRU) | 9074 us |
R5F DMA 搭配使用 | 13582 us |
我通过在 TensorBlockCpyDma()中添加这段代码,为 C7X 使用了 DRU :
app_udma_ch_handle_t handle = NULL; #ifdef C71 handle = appUdmaCopyNDGetHandle(8U); #endif if (0 == appUdmaCopy1D(handle, &prms)) { ret = true; }
您好、Brijesh、这有什么更新吗?
您好、Brijesh、您能继续吗?
在与 Kai 交谈后,她说 DRU 应该比 来自 C7X 的 memcpy 更快,所以我们需要找到 问题所在。 有任何迹象吗?
谢谢。
弗雷德
尊敬的 FredC_LT:
是的、DRU 性能应 优于 A72 memcpy 性能。 您确定测试实际使用的是 DRU 通道吗? 您能说明一下您是如何启用 DRU 通道进行此测试的吗? 如果可能、请共享该代码片段?
这是因为非 DRU 和 DRU DMA 副本的性能数据看起来非常相似(9.4ms 与9ms)、因此我怀疑是否真正使用了 DRU。
此外、在 A72侧、您如何执行 memcpy? 如何为 src 和 dst 缓冲区分配存储器? 如果可能、您能否也在此处分享该代码?
此致、
Brijesh
尊敬的 Brijesh:
要启用 DRU,我向 appUdmaCopyNDGetHandle()添加了呼叫,其中信道为8U。 下面是 我的 TensorCpyDma 函数:
typedef struct { uint32_t elems; uint32_t srcOffset; uint32_t dstOffset; } TensorCpyCfg; bool TensorBlockCpyDma(const TensorCpyCfg* const cfg, const tivx_obj_desc_tensor_t* const srcDescriptor, const tivx_obj_desc_tensor_t* const dstDescriptor, const void* restrict const srcMapped, void* restrict const dstMapped) { bool ret = false; if ( (NULL == cfg) || (NULL == srcDescriptor) || (NULL == dstDescriptor) || (NULL == srcMapped) || (NULL == dstMapped)) { VX_PRINT(VX_ZONE_ERROR, "Invalid input pointer\n"); } else if (0U == cfg->elems) { VX_PRINT(VX_ZONE_ERROR, "Empty copy\n"); } else { const uint32_t transferSize = cfg->elems * srcDescriptor->stride[0]; const uint32_t srcAvailableSize = srcDescriptor->mem_size - cfg->srcOffset * srcDescriptor->stride[0]; const uint32_t dstAvailableSize = dstDescriptor->mem_size - cfg->dstOffset * dstDescriptor->stride[0]; if ( (transferSize > srcAvailableSize) || (transferSize > dstAvailableSize)) { VX_PRINT(VX_ZONE_ERROR, "Invalid size/offset combination\n"); } else { const uint32_t srcOffset = cfg->srcOffset*srcDescriptor->stride[0]; const uint32_t dstOffset = cfg->dstOffset*dstDescriptor->stride[0]; uint64_t srcPhys = tivxMemShared2PhysPtr(srcDescriptor->mem_ptr.shared_ptr, VX_MEMORY_TYPE_HOST) + srcOffset; uint64_t dstPhys = tivxMemShared2PhysPtr(dstDescriptor->mem_ptr.shared_ptr, VX_MEMORY_TYPE_HOST) + dstOffset; uint32_t start = tivxPlatformGetTimeInUsecs(); app_udma_copy_1d_prms_t prms; appUdmaCopy1DPrms_Init(&prms); uint32_t delta = tivxPlatformGetTimeInUsecs() - start; VX_PRINT(VX_ZONE_WARNING, "appUdmaCopy1DPrms_Init = %u us\n", delta); prms.dest_addr = dstPhys; prms.src_addr = srcPhys; prms.length = transferSize; app_udma_ch_handle_t handle = NULL; #ifdef C71 start = tivxPlatformGetTimeInUsecs(); handle = appUdmaCopyNDGetHandle(8U); delta = tivxPlatformGetTimeInUsecs() - start; VX_PRINT(VX_ZONE_WARNING, "appUdmaCopyNDGetHandle = %u us\n", delta); #endif start = tivxPlatformGetTimeInUsecs(); if (0 == appUdmaCopy1D(handle, &prms)) { // void* dstVirt = (uint8_t*) dstMapped + dstOffset; // appMemCacheInv(dstVirt, transferSize); ret = true; } delta = tivxPlatformGetTimeInUsecs() - start; VX_PRINT(VX_ZONE_WARNING, "appUdmaCopy1D = %u us\n", delta); } } return ret; }
DRU 的新逻辑位于包含"#ifdef C71"的模块中
至于 A72 memcpy、则是在 TIOVX A72内核中完成的。 分配在 C++ Catch2测试中完成:
TEST_CASE("TensorCpy - Performance comparison) { REQUIRE(0 == appInit()); tivxRegisterMemoryTargetA72Kernels(); // Registers the A72 memcpy kernel std::string target = TIVX_TARGET_A72_0; std::string impl = "CPU"; const size_t COLS = 864; const size_t ROWS = 128 * 5; std::vector<vx_size> dims{COLS, ROWS}; TiovxUserKernelsTests::VxTensorWrapper src("TensorCpy src", dims); TiovxUserKernelsTests::VxTensorWrapper dst("TensorCpy dst", dims); TiovxUserKernelsTests::VxUserDataObjectWrapper<TensorCpyCfg> cfg("TensorCpyCfg"); TensorCpyGraph graph(cfg, src, dst, target); // Calls vxCreateContext() src.Allocate(graph.ctx); // Calls vxCreateTensor() dst.Allocate(graph.ctx); // Calls vxCreateTensor() float* srcptr = src.Map(VX_WRITE_ONLY); // Calls tivxMapTensorPatch() float* dstptr = dst.Map(VX_WRITE_ONLY); // Calls tivxMapTensorPatch() for (size_t i = 0; i < COLS*ROWS; i++) { srcptr[i] = i; dstptr[i] = 0; } src.Unmap(); // Calls tivxUnMapTensorPatch() dst.Unmap(); // Calls tivxUnMapTensorPatch() TensorCpyCfg cfgData; cfgData.elems = COLS * ROWS; cfgData.srcOffset = 0; cfgData.dstOffset = 0; cfg.Allocate(graph.ctx, &cfgData); // Calls vxCreateUserDataObject() graph.Allocate(); // Calls vxCreateGraph() graph.Verify(); // Calls vxVerifyGraph() graph.Run(); // Calls vxProcessGraph() graph.PrintStats(); // Calls tivx_utils_graph_perf_print() srcptr = src.Map(VX_READ_ONLY); // Calls tivxMapTensorPatch() dstptr = dst.Map(VX_READ_ONLY); // Calls tivxMapTensorPatch() REQUIRE(0 == memcmp(srcptr, dstptr, COLS*ROWS*sizeof(float))); src.Delete(); // Calls vxReleaseTensor() dst.Delete(); // Calls vxReleaseTensor() cfg.Delete(); // Calls vxReleaseUserDataObject() graph.Delete(); // Calls vxReleaseGraph(), vxReleaseContext() tivxUnRegisterMemoryTargetA72Kernels(); // Unregisters A72 memcpy kernel REQUIRE(0 == appDeInit()); }
C7X 内核的分配过程完全相同。
尊敬的 FredC_LT:
根据 以下链接上的数据表、当我们使用来自任何内核的 DRU 通道时、我们应获得大约10Gb/s 这意味着、对于上面大约0.5Mb 的数据、大约花费0.5ms。
这是它花费更多时间的原因、可能是由于未启用中断。 在 APIappUdmaCopyNDGetHandle 中、我看到通过将标志 udmaCreatePrms.enable_intr 设置为0来禁用中断、当设置为0时、任务通过执行 TaskP_yield 一直等待。 这可能无法提供良好的性能、因为可能还有其他相同或更高优先级的任务正在运行、不允许其展开。
我认为最好在启用中断的情况下检查性能、但 不确定这是否受 DRU 通道支持/验证。
此致、
Brijesh
在 appUdmaCreatePrms_Init ()(我使用)中,有以下注释:
尊敬的 FredC_LT:
确切地说、在没有中断模式的情况下、默认性能在默认代码中不是很好。
目前、只是为了测量性能、您能否 在 appUdmaTransfer 中注释掉对 TaskP_Yield API 的调用、然后检查性能?
此致、
Brijesh
尊敬的 Brijesh、我们使用 ND API 而不是1D/2D 找到了一种权变措施