MetaX-Tech Developer Forum 论坛首页
  • 沐曦开发者
search
Sign in

SunnySun

  • Members
  • Joined 2025年9月2日
  • message 帖子
  • forum 主题
  • favorite 关注者
  • favorite_border Follows
  • person_outline 详细信息

SunnySun has started 1 thread.

  • See post chevron_right
    SunnySun
    Members
    如何使用异步拷贝以达到优化的效果呢? 已解决 2025年9月2日 16:48
    #include <cooperative_groups.h>
    #include <maca_async.h>
    #include <thrust/host_vector.h>
    #include <thrust/device_vector.h>
    #include <cstdio>
    #include <cmath>
    
    namespace cg = cooperative_groups;
    
    constexpr int BLOCK_SIZE = 256;
    constexpr int TILE_SIZE = 6144;      // 每个tile的元素数量
    constexpr int NUM_TILES = 2048;         // 处理的tile数量
    constexpr int COMPUTE_ITERATIONS = 1; // 每个tile的计算工作量
    
    // compute func
    __device__ float compute_kernel(float data, int iterations) {
        float result = data;
        for (int i = 0; i < iterations; ++i) {
            result = result * 0.99f + 0.01f;
        }
        return result;
    }
    
    __global__ void async_copy_pipeline(float* global_data, float* global_output) {
        auto cta = cg::this_thread_block();
        __shared__ float smem[2][TILE_SIZE];
        // calc offset
        const int cta_offset = blockIdx.x * NUM_TILES * TILE_SIZE;
    
        float *src = global_data + cta_offset;
    
        int read_buf  = 0;   // compute
        int write_buf = 1;   // copy
    
        // the first tile 异步拷贝
        cg::memcpy_async(cta, smem[read_buf], src, sizeof(float) * TILE_SIZE);
        cg::wait(cta);
        cta.sync();
    
        for (int tile_idx = 0; tile_idx < NUM_TILES; ++tile_idx) {
            if (tile_idx < NUM_TILES - 1) {
                // 发起next tile 的异步拷贝
                float* next_src = src + (tile_idx + 1) * TILE_SIZE;
                cg::memcpy_async(cta, smem[write_buf], next_src, sizeof(float) * TILE_SIZE);
            }
            // current tile compute
            for (int i = threadIdx.x; i < TILE_SIZE; i += blockDim.x) {
                smem[read_buf][i] = compute_kernel(smem[read_buf][i], COMPUTE_ITERATIONS);
            }
    
            if (tile_idx < NUM_TILES - 1) {
                cg::wait(cta);
            }
            cta.sync();
    
            float *dst = global_output + cta_offset + tile_idx * TILE_SIZE;
            for (int i = threadIdx.x; i < TILE_SIZE; i += blockDim.x) {
                dst[i] = smem[read_buf][i];
            }
            if (tile_idx < NUM_TILES - 1) {
                int temp = read_buf;
                read_buf = write_buf;
                write_buf = temp;
            }
        }
    }
    
    // 同步版本用于对比
    __global__ void sync_copy_sequential(float* global_data, float* global_output) {
        __shared__ float smem[TILE_SIZE];
    
        float* src = global_data + blockIdx.x * NUM_TILES * TILE_SIZE;
        float* dst = global_output + blockIdx.x * NUM_TILES * TILE_SIZE;
    
        for (int tile_idx = 0; tile_idx < NUM_TILES; ++tile_idx) {
            float* current_src = src + tile_idx * TILE_SIZE;
            float* current_dst = dst + tile_idx * TILE_SIZE;
    
            //copy to smem
            for (int i = threadIdx.x; i < TILE_SIZE; i += blockDim.x) {
                smem[i] = current_src[i];
            }
            __syncthreads();
    
            for (int i = threadIdx.x; i < TILE_SIZE; i += blockDim.x) {
                smem[i] = compute_kernel(smem[i], COMPUTE_ITERATIONS);
            }
            __syncthreads();
    
            for (int i = threadIdx.x; i < TILE_SIZE; i += blockDim.x) {
                current_dst[i] = smem[i];
            }
            __syncthreads();
        }
    }
    
    int main() {
        cudaError_t cudaStatus = cudaSetDevice(0);
        const int num_blocks = 4;
        const int total_elements = num_blocks * NUM_TILES * TILE_SIZE;
    
        printf("Testing async copy pipeline with %d blocks, %d tiles per block, %d elements per tile\n",
               num_blocks, NUM_TILES, TILE_SIZE);
    
        thrust::host_vector<float> h_input(total_elements);
        thrust::host_vector<float> h_output_async(total_elements);
        thrust::host_vector<float> h_output_sync(total_elements);
        for (int j = 0; j < total_elements; ++j) h_output_async[j] = static_cast<float>(-1);
        for (int j = 0; j < total_elements; ++j) h_output_sync[j] = static_cast<float>(-1);
    
        for (int i = 0; i < total_elements; ++i) {
            h_input[i] = static_cast<float>(i) * 0.1f;
        }
    
        thrust::device_vector<float> d_input = h_input;
        thrust::device_vector<float> d_output_async = h_output_async;
        thrust::device_vector<float> d_output_sync = h_output_sync;
    
        // test async
        cudaEvent_t start_async, stop_async;
        cudaEventCreate(&start_async);
        cudaEventCreate(&stop_async);
    
        cudaEventRecord(start_async);
        async_copy_pipeline<<<num_blocks, BLOCK_SIZE>>>(
            d_input.data().get(),d_output_async.data().get()
        );
        cudaEventRecord(stop_async);
        cudaDeviceSynchronize();
    
        float async_ms = 0;
        cudaEventElapsedTime(&async_ms, start_async, stop_async);
    
        // test sync
        cudaEvent_t start_sync, stop_sync;
        cudaEventCreate(&start_sync);
        cudaEventCreate(&stop_sync);
    
        cudaEventRecord(start_sync);
        sync_copy_sequential<<<num_blocks, BLOCK_SIZE>>>(
            d_input.data().get(),d_output_sync.data().get()
        );
        cudaEventRecord(stop_sync);
        cudaDeviceSynchronize();
    
        float sync_ms = 0;
        cudaEventElapsedTime(&sync_ms, start_sync, stop_sync);
    
        // 验证结果一致性
        thrust::host_vector<float> h_result_async = d_output_async;
        thrust::host_vector<float> h_result_sync = d_output_sync;
    
        bool results_match = true;
        for (int i = 0; i < total_elements; ++i) {
            if (fabs(h_result_async[i] - h_result_sync[i]) > 1e-6) {
                results_match = false;
                break;
            }
        }
    
        printf("Results: %s\n", results_match ? "MATCH" : "MISMATCH");
        printf("Async pipeline time: %.3f ms\n", async_ms);
        printf("Sync sequential time: %.3f ms\n", sync_ms);
        printf("Speedup: %.2fx\n", sync_ms / async_ms);
    
        // clear
        cudaEventDestroy(start_async);
        cudaEventDestroy(stop_async);
        cudaEventDestroy(start_sync);
        cudaEventDestroy(stop_sync);
    
        return 0;
    }
    

    上面的代码是用于 测试异步拷贝加计算的运行时间 以及 同步拷贝加计算的运行时间,预期结果是使用异步拷贝性能优于同步拷贝,但是在MetaX C500上测试,结果如下(结果显示异步拷贝耗时更长):

    Testing async copy pipeline with 4 blocks, 2048 tiles per block, 6144 elements per tile
    Results: MATCH
    Async pipeline time: 30.342 ms
    Sync sequential time: 9.460 ms
    Speedup: 0.31x

    同样的代码,在L20上测试,结果如下(L20上异步拷贝的耗时比同步短):

    Testing async copy pipeline with 4 blocks, 2048 tiles per block, 6144 elements per tile
    Results: MATCH
    Async pipeline time: 4.221 ms
    Sync sequential time: 17.118 ms
    Speedup: 4.06x

    代码中用到了异步拷贝,主要参考developer.metax-tech.com/api/client/document/preview/559/C500_MXMACAC%2B%2BProgrammingGuide_CN.html#ehg6356t2io61 中说明的使用方法

    问题:为什么在C500上异步拷贝的性能比同步的还要差呢?请问是异步拷贝的接口使用有问题还是什么原因呢?请指教,谢谢

  • 沐曦开发者论坛
powered by misago