首页 > 编程语言 >基于oneAPI的并行算法实践

基于oneAPI的并行算法实践

时间:2023-12-03 22:13:09浏览次数:32  
标签:std sycl 并行算法 实践 queue item oneAPI vector size

本文介绍了利用oneAPI,使用sycl编程实现并行算法,完成了矩阵乘法、归并排序、图像卷积三个任务的过程。

矩阵乘法

在此任务中,我们使用sycl编写并行计算的内核。为了提高局部计算效率,我们使用共享内存存储部分矩阵数据。


std::vector<std::vector<float>> matrixMultiply(const std::vector<std::vector<float>> &A, const std::vector<std::vector<float>> &B) {
    // Create a SYCL queue
    sycl::queue queue(sycl::default_selector{});

    // Flatten 2D matrices to 1D vectors
    std::vector<float> flatA;
    std::vector<float> flatB;
    for (const auto &row : A)
        flatA.insert(flatA.end(), row.begin(), row.end());
    for (const auto &row : B)
        flatB.insert(flatB.end(), row.begin(), row.end());

    // Create buffers for flattened matrices A, B, and C
    sycl::buffer<float, 1> bufferA(flatA.data(), sycl::range<1>(M * K));
    sycl::buffer<float, 1> bufferB(flatB.data(), sycl::range<1>(K * N));
    sycl::buffer<float, 1> bufferC(sycl::range<1>(M * N));

    // Submit a command group to the queue
    queue.submit([&](sycl::handler &cgh) {
        // Accessors for the buffers
        auto a = bufferA.get_access<sycl::access::mode::read>(cgh);
        auto b = bufferB.get_access<sycl::access::mode::read>(cgh);
        auto c = bufferC.get_access<sycl::access::mode::write>(cgh);

        // Shared memory for each work group
        sycl::accessor<float, 2, sycl::access::mode::read_write, sycl::access::target::local>
            localMem(sycl::range<2>(blockSize, blockSize), cgh);

        // Define a 2D range for global size
        sycl::range<2> globalSize(M, N);

        cgh.parallel_for<class matrixMultiplyKernel>(sycl::nd_range<2>(globalSize, sycl::range<2>(blockSize, blockSize)),
                                                     [=](sycl::nd_item<2> item) {
                                                         int row = item.get_global_id(0);
                                                         int col = item.get_global_id(1);

                                                         float sum = 0.0f;

                                                         // Loop over blocks
                                                         for (int kBlock = 0; kBlock < K; kBlock += blockSize) {
                                                             // Load block of A and B into shared memory
                                                             localMem[item.get_local_id(0)][item.get_local_id(1)] = a[row * K + kBlock + item.get_local_id(1)];
                                                             item.barrier(sycl::access::fence_space::local_space);
                                                             localMem[item.get_local_id(0)][item.get_local_id(1)] *= b[(kBlock + item.get_local_id(0)) * N + col];
                                                             item.barrier(sycl::access::fence_space::local_space);

                                                             // Perform block-level computation
                                                             for (int i = 0; i < blockSize; ++i) {
                                                                 sum += localMem[item.get_local_id(0)][i] * localMem[i][item.get_local_id(1)];
                                                             }
                                                             item.barrier(sycl::access::fence_space::local_space);
                                                         }

                                                         // Write result to global memory
                                                         c[row * N + col] = sum;
                                                     });
    }).wait();  // Wait for the command group to finish

    // Copy result from bufferC to a 2D vector and return
    std::vector<std::vector<float>> result(M, std::vector<float>(N, 0.0f));
    sycl::host_accessor resultAccessor(bufferC, sycl::read_only);
    for (int i = 0; i < M; ++i) {
        for (int j = 0; j < N; ++j) {
            result[i][j] = resultAccessor[i * N + j];
        }
    }

    return result;
}

归并排序

归并排序是一种经典的排序算法,通过分治的思想将问题分解成小问题,然后将小问题的解合并起来。在数据规模较大时,我们可以引入并行计算,通过将排序任务划分成小块并使用并行计算资源来同时处理这些块,从而提高算法效率。


// Function to merge two sorted halves of a vector
template <typename T>
void merge(std::vector<T> &data, std::vector<T> &temp, size_t left, size_t middle, size_t right) {
    size_t i = left;
    size_t j = middle + 1;
    size_t k = left;

    while (i <= middle && j <= right) {
        if (data[i] <= data[j]) {
            temp[k++] = data[i++];
        } else {
            temp[k++] = data[j++];
        }
    }

    while (i <= middle) {
        temp[k++] = data[i++];
    }

    while (j <= right) {
        temp[k++] = data[j++];
    }

    // Copy merged elements back to the original vector
    for (size_t l = left; l <= right; ++l) {
        data[l] = temp[l];
    }
}

// Recursive function for parallel merge sort
template <typename T>
void mergeSortRecursive(sycl::queue &queue, std::vector<T> &data, std::vector<T> &temp, size_t left, size_t right) {
    if (left < right) {
        size_t middle = (left + right) / 2;

        // Create SYCL buffers for data and temp vectors
        sycl::buffer<T, 1> bufferData(data.data(), sycl::range<1>(data.size()));
        sycl::buffer<T, 1> bufferTemp(temp.data(), sycl::range<1>(temp.size()));

        // Submit a command group for parallel merge sort
        queue.submit([&](sycl::handler &cgh) {
            auto dataAccessor = bufferData.get_access<sycl::access::mode::read_write>(cgh);
            auto tempAccessor = bufferTemp.get_access<sycl::access::mode::read_write>(cgh);

            cgh.parallel_for<class mergeSortKernel>(sycl::range<1>(1), [=](sycl::item<1> item) {
                // Recursive call for left half
                mergeSortRecursive(queue, data, temp, left, middle);

                // Recursive call for right half
                mergeSortRecursive(queue, data, temp, middle + 1, right);

                // Merge the two halves
                merge(data, temp, left, middle, right);
            });
        });
    }
}

// Parallel merge sort entry function
template <typename T>
void parallelMergeSort(std::vector<T> &data) {
    // Create a SYCL queue
    sycl::queue queue(sycl::default_selector{});

    // Create a temporary vector for merging
    std::vector<T> temp(data.size());

    // Perform parallel merge sort
    mergeSortRecursive(queue, data, temp, 0, data.size() - 1);
}

图像卷积

在本任务中,我们将图片分成许多小块,并行地进行卷积操作,提高了算法的效率。


// Function to perform image convolution using SYCL
std::vector<std::vector<float>> imageConvolution(const std::vector<std::vector<float>> &image,
                                                 const std::vector<std::vector<float>> &kernel) {
    const size_t imageHeight = image.size();
    const size_t imageWidth = image[0].size();
    const size_t kernelSize = kernel.size();
    const size_t padding = kernelSize / 2;

    // Create a SYCL queue
    sycl::queue queue(sycl::default_selector{});

    // Create buffers for image and kernel
    sycl::buffer<float, 2> bufferImage(image.data(), sycl::range<2>(imageHeight, imageWidth));
    sycl::buffer<float, 2> bufferKernel(kernel.data(), sycl::range<2>(kernelSize, kernelSize));

    // Create a buffer for the result image
    sycl::buffer<float, 2> bufferResult(sycl::range<2>(imageHeight, imageWidth));

    // Submit a command group to the queue
    queue.submit([&](sycl::handler &cgh) {
        auto imageAccessor = bufferImage.get_access<sycl::access::mode::read>(cgh);
        auto kernelAccessor = bufferKernel.get_access<sycl::access::mode::read>(cgh);
        auto resultAccessor = bufferResult.get_access<sycl::access::mode::write>(cgh);

        cgh.parallel_for<class imageConvolutionKernel>(sycl::range<2>(imageHeight, imageWidth),
                                                       [=](sycl::item<2> item) {
                                                           float sum = 0.0f;

                                                           // Iterate over the kernel
                                                           for (size_t i = 0; i < kernelSize; ++i) {
                                                               for (size_t j = 0; j < kernelSize; ++j) {
                                                                   // Coordinates in the original image
                                                                   size_t x = item[0] + i - padding;
                                                                   size_t y = item[1] + j - padding;

                                                                   // Apply padding using nearest mode
                                                                   x = sycl::clamp(x, size_t(0), imageHeight - 1);
                                                                   y = sycl::clamp(y, size_t(0), imageWidth - 1);

                                                                   // Convolution operation
                                                                   sum += imageAccessor[x][y] * kernelAccessor[i][j];
                                                               }
                                                           }

                                                           // Write the result to the buffer
                                                           resultAccessor[item] = sum;
                                                       });
    }).wait();  // Wait for the command group to finish

    // Copy result from buffer to a 2D vector and return
    std::vector<std::vector<float>> result(imageHeight, std::vector<float>(imageWidth, 0.0f));
    sycl::host_accessor resultAccessor(bufferResult, sycl::read_only);
    for (size_t i = 0; i < imageHeight; ++i) {
        for (size_t j = 0; j < imageWidth; ++j) {
            result[i][j] = resultAccessor[i][j];
        }
    }

    return result;
}

标签:std,sycl,并行算法,实践,queue,item,oneAPI,vector,size
From: https://www.cnblogs.com/kazoeyakuman/p/17873901.html

相关文章

  • Jenkins自动化构建Vue项目的实践
    在现代的Web开发中,Vue.js已经成为一种非常流行的JavaScript框架。为了更高效地管理和部署Vue.js项目,使用自动化构建工具是至关重要的。Jenkins作为一款强大的持续集成和持续部署(CI/CD)工具,为我们提供了一种便捷的方式来自动化构建Vue.js项目。本文将介绍如何在Jenkins中配置和使用自......
  • Spring实践之自定义命名空间并解析
    自定义一个命名空间1、新建一个空项目,在resources/META-INF目录下新建一个spring.handlers文件,内容如下:http\://open.harvey.com/schema/dog=com.harvey.open.annotation.spring.DogNamespaceHandler文件内容为一个键值对。key为自定义命名空间:http://open.harvey.com/sc......
  • 软件工程读后感8-代码阅读方法与实践
    最近,我阅读了代码阅读方法与实践的下一部分。在C程序中,指针一般用来:构造链式数据结构、引用动态分配的数据结构、实现引用调用、访问和迭代数据元素、传递数组参数、引用函数、作为其他值的别名、代表字符串、直接访问系统内存。过去,我对于指针的作用的了解不够,将来,我会尽可能的多......
  • 抖音订单接口在电商行业中的重要性及实践应用
    一、引言随着移动互联网的快速发展,短视频平台抖音已经成为人们日常生活中不可或缺的一部分。越来越多的商家开始利用抖音平台推广和销售商品,从而实现商业变现。在这个过程中,抖音订单接口起到了至关重要的作用。本文将详细探讨抖音订单接口在电商行业中的重要性,并通过实践应用案例和......
  • React 整洁代码的 10 个最佳实践(译)
    作者:大家的林语冰链接:https://www.zhihu.com/question/36516604/answer/3279585231来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。React整洁代码的10个最佳实践(译)免责声明本文属于是语冰的直男翻译了属于是,仅供粉丝参考,英文原味版请临幸Top1......
  • 代码阅读方法与实践
    《代码阅读方法与实践》阅读笔记总体印象:《代码阅读方法与实践》是一本引人深思的书籍,旨在帮助开发者更有效地阅读、理解和应用代码。作者以清晰而生动的语言,深入浅出地探讨了代码阅读的方法和实践,为读者提供了宝贵的思考和工作工具。核心观点:代码即文档:作者强调代码本身就......
  • PieCloudDB Database 云上商业智能的最佳实践
    「商业智能(BusinessIntelligence,BI)」这个概念最早是Gartner在上个世纪九十年代提出的,它认为从功能上来说,商业智能是一种解决方案,其关键是处理企业来自多个来源的各种数据,提取有用的数据并清理,然后经过抽取(Extraction)、转换(Transformation)和加载(Load),即ETL过程,合并到一个企业级......
  • TiDB 在 WPS丨分享业务双机房建设实践
    WPSOffice是一站式办公服务平台,全球范围内,每天有超过5亿个文件在WPSOffice平台上被创建、编辑、和分享。本文分享了WPS在TiDB版本升级和双机房改造中的实践经验。作者:曹鹏,WPS云平台运维Leader;肖尚武,WPS云平台DBA;庾俊,WPS云文档研发。背景WPS算是国内TiDB较早的......
  • TiDB 在京东云丨TiDB SQL 优化最佳实践
    本文作者:赵玉龙京东云与PingCAP深度合作,联合推出了一款云上分布式数据库产品,向京东云用户提供云上的TiDB服务。它可以同时支持OLTP和OLAP混合负载场景,实现了自动水平伸缩,强一致性的分布式事务,部署简单,在线异步变更表结构不影响业务。由于TiDB兼容MySQL5.7协议、MySQL......
  • day09 Helm开发与实践-基于Helm的方式运维管理应用 (3.2-3.3)
    一、Helm开发与实践1、HelmChart详解1.1Chart目录结果#helmcreatenginxCreatingnginx#treenginxnginx├──charts├──Chart.yaml├──templates│  ├──deployment.yaml│  ├──_helpers.tpl│  ├──hpa.yaml│  ├──ingre......