【问题标题】:Cost functional calculation for global optimization in CUDACUDA 中全局优化的成本函数计算
【发布时间】:2012-07-04 08:21:03
【问题描述】:

我正在尝试使用n 参数(Xn优化函数(比如找到最小值)。所有Xi 都绑定在某个范围内(例如-200200),如果任何参数离开此范围,函数会非常快地变为无穷大。但是,n 可能很大(从20 到大约60-70)并且计算它的值需要很长时间。

我不认为函数的细节有很大的相关性,但这里有一些:它由20-30 更小的函数(所有不同的)的加权和组成,它们在它们的部分由反正弦函数符号下的点积和组成(arcsinarccosarctan 等)。类似arcsin(X1 . X2) + arcsin(X4 . X7) + ...

该函数通常具有许多局部最小值,因此(朴素)共轭梯度或准牛顿等方法是无用的。搜索整个域蛮力太慢了。

我最初的想法是结合遗传算法使用某种大规模并行化,该算法在函数域中的不同位置执行许多搜索,并定期检查一些搜索是否达到局部最小值。如果是,它会比较它们并丢弃除最小的结果之外的所有结果,并继续搜索,直到找到相当小的值。

我的两个问题是:

1) 是否可以在 CUDA 或类似技术中实现此问题? CUDA 能足够快地计算出这样一个函数的值吗?

2) 在多核 PC(具有 12 个以上内核)上实现该问题会更好/更快吗?

【问题讨论】:

  • 1) 我当然认为这是可能的。定义足够快。 2)我认为没有任何方法可以知道这一点,而无需实际编写算法并执行一些性能测试。变量太多了。
  • 我还没有编写过 OpenCL,但它可以在 CPU 和 GPU 上运行。英特尔、AMD、IBM 和 nVidia 发货 OpenCL 'SDKs' which compile to their respective products。为什么要使用 CUDA 并将自己限制在 nVidia GPU 上? ;)
  • @ArjunShankar 我曾经问过自己这个问题。然后我试用了 CUDA,并意识到使用它进行编程要舒服得多(加上功能强大且集成良好的推力库,其中包含极值函数),我只是变得更有效率。此外,有关程序运行的实际硬件架构的知识对于优化也是至关重要的。
  • 没错,我还没有开始使用它,但它似乎比其他所有东西都要好得多(截至目前)。这里重要的不是 CUDA,而是大规模并行原则。

标签: optimization cuda genetic-algorithm nonlinear-optimization cub


【解决方案1】:

当然可以在 GPU 上实现全局优化算法,但您可能必须修改算法以获得最佳性能。我个人已经在 GPU 上实现了大约六种基于人口的元启发式算法,并且相对于多线程 CPU 可以获得出色的加速。

随着基于人口的算法迭代几代,您可能会发现人口规模成为您表现的限制因素。如果您的目标函数不容易并行化,那么并行线程的最大数量通常是总体中候选解决方案的数量。 GPU 在至少有数万个同时线程的情况下工作得最好,由于人口惯性和其他影响,这对于基于人口的算法并不总是实用的。

您可以通过并行运行多个优化实例来在一定程度上绕过人口限制,每个实例都从不同的随机种子开始。此外,您可以安排并行实例通过某种网络拓扑进行通信(这将是一个孤岛模型算法),以便并行实例可以协作演化复杂问题的解决方案。我实际上已经在 OpenCL 中为我的 MScE 论文实现了这一点。

总的来说,以下是您问题的简洁答案:

1) 是的,您可以在 CUDA 或类似技术中实现此功能。您的加速将取决于您的目标函数的可并行化程度以及您希望同时运行多少个并行实例。 2) 由于现有库范围更广,而且 CPU 编程模型在概念上比 GPU 模型更简单,因此在 CPU 上实现几乎肯定会更快。它是否“更好”取决于您的价值。

这仍然是一个活跃的研究领域,与构建多核 CPU 实现相比,构建一个有效的 GPU 实现可能需要更长的时间。如果实现时间是您最关心的问题,我建议您查看 PaGMO 项目(及其 Python 绑定 PyGMO),它是岛模型优化器的出色实现,具有广泛的局部和全局优化功能包括。可以为这些岛屿分配任意算法以独立运行,并且您可以准确指定它们的通信方式以协同优化您的目标函数。

http://sourceforge.net/apps/mediawiki/pagmo/index.php?title=Main_Page

您的问题属于我的研究领域,如果您需要,我很乐意为您提供进一步的帮助。

【讨论】:

  • 你,先生,真棒。这正是我需要的答案,我会尽快标记它。但是,我想问你几件事:1)“目标函数”是指要优化的函数,对吗? 2)“人口惯性”是什么意思 3)我主要关心的不是实现时间,但只有当我确定它值得时,我才会花更多时间在 GPU 上实现算法。因此,根据您的经验,为 CUDA/类似技术编程要慢多少/难度多少?
  • 很高兴为您服务。 1)是的,“目标函数”是指“要优化的函数”。这是优化领域中常用的术语。 2)例如,如果您使用的是遗传算法,则人口众多意味着存在大量候选解决方案,这些解决方案需要变异或被子代替换。大量人口会减慢收敛速度,甚至完全阻止收敛。 3) 对我来说,我通常会花费 2-3 倍的时间来实现 GPU 和 CPU 程序。对于初学者来说,甚至可能需要几天/几周的时间才能开始编码。
  • 听起来令人沮丧。我想您无法调试代码,就像使用经典的本机编程语言一样。我想我会用 PaGMO 试一试,它看起来不错。我所知道的是,如果您不缓存数据传输,或者例如复制过多的数据,GPU 编程甚至可能比 CPU 慢。我希望描述一个函数不应该占用太多空间,所以它不应该减慢 GPU 处理速度。你能给我介绍一下基因编程的介绍吗?有了基本的原则,例如如何管理人口?
  • 你可以调试代码(developer.download.nvidia.com/compute/DevZone/docs/html/C/doc/…),只是更难做。我认为有效的数据管理是 GPU 编程中最困难的部分。有几种不同类型的内存,通常需要对代码和算法进行大量更改才能全部尝试。如果您想编写自己的遗传算法,我强烈建议您先在 CPU 上进行。这个网站看起来像是对遗传算法的一个很好的介绍:obitko.com/tutorials/genetic-algorithms
【解决方案2】:

CUDA 能否以足够快的速度计算这样一个函数的值?

许多成本泛函以一定数量的项的总和的形式表示。例如:

球体函数

Rosenbrock 函数

Styblinski-Tang 函数

在所有这些情况下,成本函数的评估可以通过缩减来执行,或者更好的是,先转换然后再缩减。

CUDA Thrust 有thrust::transform_reduce 肯定可以服务于范围,但当然您可以设置自己的转换+归约例程。

下面,我将提供一个示例,说明如何使用 CUDA Thrust 或 CUDA 示例提供的缩减例程的自定义版本来计算 Rosenbrock 泛函。在后一种情况下,如果定义了EXTERNAL关键字,或者在自定义@的编译单元中定义并编译了转换函数,则将指向__device__转换函数的指针传递给自定义transform_reduce函数987654330@例程。

Kepler K20c 卡在非 EXTERNAL 情况下的一些性能结果:

N =   90000       Thrust = 0.055ms       Customized = 0.059ms
N =  900000       Thrust = 0.67ms        Customized = 0.14ms
N = 9000000       Thrust = 0.85ms        Customized = 0.87ms

这里是代码。计时功能请看OrangeOwlSolutions/TimingOrangeOwlSolutions/CUDA_Utilitiesgithub项目。

请注意,需要单独编译。

kernel.cu

// --- Requires separate compilation

#include <stdio.h>

#include <thrust/device_vector.h>

#include "transform_reduce.cuh"
#include "Utilities.cuh"
#include "TimingCPU.h"
#include "TimingGPU.cuh"

/***************************************/
/* COST FUNCTION - GPU/CUSTOMIZED CASE */
/***************************************/
// --- Transformation function
__device__ __forceinline__ float transformation(const float * __restrict__ x, const int i) { return (100.f * (x[i+1] - x[i] * x[i]) * (x[i+1] - x[i] * x[i]) + (x[i] - 1.f) * (x[i] - 1.f)) ; }
// --- Device-side function pointer
__device__ pointFunction_t dev_pfunc = transformation;

/***********************************/
/* COST FUNCTION - GPU/THRUST CASE */
/***********************************/
struct CostFunctionStructGPU{
template <typename Tuple>
    __host__ __device__ float operator()(Tuple a) {

        float temp1 = (thrust::get<1>(a) - thrust::get<0>(a) * thrust::get<0>(a));
        float temp2 = (thrust::get<0>(a) - 1.f);

        return 100.f * temp1 * temp1 + temp2 * temp2;
    }
};

/********/
/* MAIN */
/********/
int main()
{
    const int N = 90000000;

    float *x = (float *)malloc(N * sizeof(float));
    for (int i=0; i<N; i++) x[i] = 3.f;

    float *d_x; gpuErrchk(cudaMalloc((void**)&d_x, N * sizeof(float)));
    gpuErrchk(cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice));

    /************************************************/
    /* "CUSTOMIZED" DEVICE-SIDE TRANSFORM REDUCTION */
    /************************************************/
    float customizedDeviceResult = transform_reduce(d_x, N - 1, &dev_pfunc);
    TimingGPU timerGPU;
    timerGPU.StartCounter();
    customizedDeviceResult = transform_reduce(d_x, N - 1, &dev_pfunc);
    printf("Timing for 'customized', device-side transform reduction = %f\n", timerGPU.GetCounter());
    printf("Result for 'customized', device-side transform reduction = %f\n", customizedDeviceResult);
    printf("\n\n");

    /************************************************/
    /* THRUST-BASED DEVICE-SIDE TRANSFORM REDUCTION */
    /************************************************/
    thrust::device_vector<float> d_vec(N,3.f);

    timerGPU.StartCounter();
    float ThrustResult = thrust::transform_reduce(thrust::make_zip_iterator(thrust::make_tuple(d_vec.begin(), d_vec.begin() + 1)), thrust::make_zip_iterator(thrust::make_tuple(d_vec.begin() + N - 1, d_vec.begin() + N)), CostFunctionStructGPU(), 0.f, thrust::plus<float>());
    printf("Timing for Thrust-based, device-side transform reduction = %f\n", timerGPU.GetCounter());
    printf("Result for Thrust-based, device-side transform reduction = %f\n", ThrustResult);
    printf("\n\n");

    /*********************************/
    /* HOST-SIDE TRANSFORM REDUCTION */
    /*********************************/
//  thrust::host_vector<float> h_vec(d_vec);
        //sum_host = sum_host + transformation(thrust::raw_pointer_cast(h_vec.data()), i);

    TimingCPU timerCPU;
    timerCPU.StartCounter();
    float sum_host = 0.f;
    for (int i=0; i<N-1; i++) {
        float temp = (100.f * (x[i+1] - x[i] * x[i]) * (x[i+1] - x[i] * x[i]) + (x[i] - 1.f) * (x[i] - 1.f));
        sum_host = sum_host + temp;
        //printf("%i %f %f\n", i, temp, sum_host);
    }

    printf("Timing for host-side transform reduction = %f\n", timerCPU.GetCounter());
    printf("Result for host-side transform reduction = %f\n", sum_host);
    printf("\n\n");

    sum_host = 0.f;
    float c        = 0.f;
    for (int i=0; i<N-1; i++) {
        float temp = (100.f * (x[i+1] - x[i] * x[i]) * (x[i+1] - x[i] * x[i]) + (x[i] - 1.f) * (x[i] - 1.f)) - c;
        float t    = sum_host + temp;
        c          = (t - sum_host) - temp;
        sum_host = t;
    }

    printf("Result for host-side transform reduction = %f\n", sum_host);

//  cudaDeviceReset();

}

transform_reduce.cuh

#ifndef TRANSFORM_REDUCE_CUH
#define TRANSFORM_REDUCE_CUH

// --- Function pointer type
// --- Complete with your own favourite instantiations
typedef float(*pointFunction_t)(const float * __restrict__, const int);

template <class T> T transform_reduce(T *, unsigned int, pointFunction_t *);

#endif

transform_reduce.cu

#include <stdio.h>

#include "Utilities.cuh"
#include "transform_reduce.cuh"

#define BLOCKSIZE 512
#define warpSize 32

// --- Host-side function pointer
pointFunction_t h_pfunc;

// --- Uncomment if you want to apply CUDA error checking to the kernel launches
//#define DEBUG

//#define EXTERNAL

/*******************************************************/
/* CALCULATING THE NEXT POWER OF 2 OF A CERTAIN NUMBER */

/*******************************************************/
unsigned int nextPow2(unsigned int x)
{
    --x;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return ++x;
}

/*************************************/
/* CHECK IF A NUMBER IS A POWER OF 2 */
/*************************************/
// --- Note: although x = 1 is a power of 2 (1 = 2^0), this routine returns 0 for x == 1.
bool isPow2(unsigned int x) {
    if (x == 1) return 0;
    else        return ((x&(x-1))==0);
}

/***************************/
/* TRANSFORMATION FUNCTION */
/***************************/
template <class T>
__host__ __device__ __forceinline__ T transformation(const T * __restrict__ x, const int i) { return ((T)100 * (x[i+1] - x[i] * x[i]) * (x[i+1] - x[i] * x[i]) + (x[i] - (T)1) * (x[i] - (T)1)) ; }

/********************/
/* REDUCTION KERNEL */
/********************/
/*
    This version adds multiple elements per thread sequentially.  This reduces the overall
    cost of the algorithm while keeping the work complexity O(n) and the step complexity O(log n).
    (Brent's Theorem optimization)

    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.
    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.
    If blockSize > 32, allocate blockSize*sizeof(T) bytes.
*/
template <class T, unsigned int blockSize, bool nIsPow2>
__global__ void reductionKernel(T *g_idata, T *g_odata, unsigned int N, pointFunction_t pPointTransformation)
{
    extern __shared__ T sdata[];

    unsigned int tid    = threadIdx.x;                              // Local thread index
    unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;       // Global thread index - Fictitiously double the block dimension
    unsigned int gridSize = blockSize*2*gridDim.x;

    // --- Performs the first level of reduction in registers when reading from global memory on multiple elements per thread.
    //     More blocks will result in a larger gridSize and therefore fewer elements per thread
    T mySum = 0;

    while (i < N) {
#ifdef EXTERNAL
        mySum += (*pPointTransformation)(g_idata, i);
#else
        mySum += transformation(g_idata, i);
#endif
        // --- Ensure we don't read out of bounds -- this is optimized away for powerOf2 sized arrays
        if (nIsPow2 || i + blockSize < N)
#ifdef EXTERNAL
            mySum += (*pPointTransformation)(g_idata, i+blockSize);
#else
            mySum += transformation(g_idata, i+blockSize);
#endif
        i += gridSize; }

    // --- Each thread puts its local sum into shared memory
    sdata[tid] = mySum;
    __syncthreads();

    // --- Reduction in shared memory. Fully unrolled loop.
    if ((blockSize >= 512) && (tid < 256)) sdata[tid] = mySum = mySum + sdata[tid + 256];
    __syncthreads();

    if ((blockSize >= 256) && (tid < 128)) sdata[tid] = mySum = mySum + sdata[tid + 128];
     __syncthreads();

    if ((blockSize >= 128) && (tid <  64)) sdata[tid] = mySum = mySum + sdata[tid +  64];
    __syncthreads();

#if (__CUDA_ARCH__ >= 300 )
    // --- Single warp reduction by shuffle operations
    if ( tid < 32 )
    {
        // --- Last iteration removed from the for loop, but needed for shuffle reduction
        mySum += sdata[tid + 32];
        // --- Reduce final warp using shuffle
        for (int offset = warpSize/2; offset > 0; offset /= 2) mySum += __shfl_down(mySum, offset);
        //for (int offset=1; offset < warpSize; offset *= 2) mySum += __shfl_xor(mySum, i);
    }
#else
    // --- Reduction within a single warp. Fully unrolled loop.
    if ((blockSize >=  64) && (tid < 32)) sdata[tid] = mySum = mySum + sdata[tid + 32];
    __syncthreads();

    if ((blockSize >=  32) && (tid < 16)) sdata[tid] = mySum = mySum + sdata[tid + 16];
    __syncthreads();

    if ((blockSize >=  16) && (tid <  8)) sdata[tid] = mySum = mySum + sdata[tid +  8];
    __syncthreads();

    if ((blockSize >=   8) && (tid <  4)) sdata[tid] = mySum = mySum + sdata[tid +  4];
     __syncthreads();

    if ((blockSize >=   4) && (tid <  2)) sdata[tid] = mySum = mySum + sdata[tid +  2];
    __syncthreads();

    if ((blockSize >=   2) && ( tid < 1)) sdata[tid] = mySum = mySum + sdata[tid +  1];
    __syncthreads();
#endif

    // --- Write result for this block to global memory. At the end of the kernel, global memory will contain the results for the summations of
    //     individual blocks
    if (tid == 0) g_odata[blockIdx.x] = mySum;
}

/******************************/
/* REDUCTION WRAPPER FUNCTION */
/******************************/
template <class T>
T transform_reduce_inner(T *g_idata, unsigned int N, pointFunction_t h_pfunc) {

    // --- Reduction parameters
    const int NumThreads    = (N < BLOCKSIZE) ? nextPow2(N) : BLOCKSIZE;
    const int NumBlocks     = (N + NumThreads - 1) / NumThreads;
    const int smemSize      = (NumThreads <= 32) ? 2 * NumThreads * sizeof(T) : NumThreads * sizeof(T);

    // --- Device memory space where storing the partial reduction results
    T *g_odata; gpuErrchk(cudaMalloc((void**)&g_odata, NumBlocks * sizeof(T)));

    if (isPow2(N)) {
        switch (NumThreads) {
            case 512: reductionKernel<T, 512, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 256: reductionKernel<T, 256, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 128: reductionKernel<T, 128, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 64:  reductionKernel<T,  64, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 32:  reductionKernel<T,  32, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 16:  reductionKernel<T,  16, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  8:  reductionKernel<T,   8, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  4:  reductionKernel<T,   4, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  2:  reductionKernel<T,   2, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  1:  reductionKernel<T,   1, true><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
        }
#ifdef DEBUG
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
#endif
    }
    else {
        switch (NumThreads) {
            case 512: reductionKernel<T, 512, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 256: reductionKernel<T, 256, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 128: reductionKernel<T, 128, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 64:  reductionKernel<T,  64, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 32:  reductionKernel<T,  32, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case 16:  reductionKernel<T,  16, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  8:  reductionKernel<T,   8, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  4:  reductionKernel<T,   4, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  2:  reductionKernel<T,   2, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
            case  1:  reductionKernel<T,   1, false><<< NumBlocks, NumThreads, smemSize>>>(g_idata, g_odata, N, h_pfunc); break;
        }
#ifdef DEBUG
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
#endif
    }

    // --- The last part of the reduction, which would be expensive to perform on the device, is executed on the host
    T *host_vector = (T *)malloc(NumBlocks * sizeof(T));
    gpuErrchk(cudaMemcpy(host_vector, g_odata, NumBlocks * sizeof(T), cudaMemcpyDeviceToHost));

    T sum_transformReduce = (T)0;
    for (int i=0; i<NumBlocks; i++) sum_transformReduce = sum_transformReduce + host_vector[i];

    return sum_transformReduce;
}

template <class T>
T transform_reduce(T *g_idata, unsigned int N, pointFunction_t *dev_pfunc) {
#ifdef EXTERNAL
    gpuErrchk(cudaMemcpyFromSymbol(&h_pfunc, *dev_pfunc, sizeof(pointFunction_t)));
#endif
    T customizedDeviceResult = transform_reduce_inner(g_idata, N, h_pfunc);
    return customizedDeviceResult;
}


// --- Complete with your own favourite instantiations
template float transform_reduce(float *, unsigned int, pointFunction_t *);

【讨论】:

    【解决方案3】:

    我上面的回答主要适用于具有大量未知数的问题,这些问题通常由局部优化算法处理。我将把它留在这里,以供其他用户参考。

    正如您所提到的,您正在处理60-70未知数,这种情况可以通过全局优化算法更轻松地管理。

    如上所述,成本泛函通常由求和组成,因此它们的计算相当于后续的变换和归约操作。有了这么多的未知数,减少共享内存可能是一个有趣的选择。幸运的是,CUB 提供了减少共享内存的原语。

    这是一个工作示例,说明如何使用 CUB 计算具有中等数量未知数的问题的大量成本函数值。本例中的成本泛函选择为 Rastrigin 函数,但该示例只需更改相应的__device__ 函数即可适用于其他成本泛函。

    #include <cub/cub.cuh>
    #include <cuda.h>
    
    #include "Utilities.cuh"
    
    #include <iostream>
    
    #define BLOCKSIZE           256
    
    const int N = 4096;
    
    /************************/
    /* RASTRIGIN FUNCTIONAL */
    /************************/
    __device__ float rastrigin(float x) {
    
        return x * x - 10.0f * cosf(2.0f * x) + 10.0f;
    
    }
    
    /******************************/
    /* TRANSFORM REDUCTION KERNEL */
    /******************************/
    __global__ void CostFunctionalCalculation(const float * __restrict__ indata, float * __restrict__ outdata) {
    
        unsigned int tid = threadIdx.x + blockIdx.x * gridDim.x;
    
        // --- Specialize BlockReduce for type float. 
        typedef cub::BlockReduce<float, BLOCKSIZE> BlockReduceT;
    
        __shared__ typename BlockReduceT::TempStorage  temp_storage;
    
        float result;
        if(tid < N) result = BlockReduceT(temp_storage).Sum(rastrigin(indata[tid]));
    
        if(threadIdx.x == 0) outdata[blockIdx.x] = result;
    
        return;
    }
    
    /********/
    /* MAIN */
    /********/
    int main() {
    
        // --- Allocate host side space for 
        float *h_data       = (float *)malloc(N               * sizeof(float));
        float *h_result     = (float *)malloc((N / BLOCKSIZE) * sizeof(float));
    
        float *d_data;      gpuErrchk(cudaMalloc(&d_data,   N               * sizeof(float)));
        float *d_result;    gpuErrchk(cudaMalloc(&d_result, (N / BLOCKSIZE) * sizeof(float)));
    
        for (int i = 0; i < N; i++) {
            h_data[i] = 1.f;
        }
    
        gpuErrchk(cudaMemcpy(d_data, h_data, N * sizeof(float), cudaMemcpyHostToDevice));
    
        CostFunctionalCalculation<<<iDivUp(N, BLOCKSIZE), BLOCKSIZE>>>(d_data, d_result);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
    
        gpuErrchk(cudaMemcpy(h_result, d_result, (N / BLOCKSIZE) * sizeof(float), cudaMemcpyDeviceToHost));
    
        std::cout << "output: \n";
        for (int k = 0; k < N / BLOCKSIZE; k++) std::cout << k << " " << h_result[k] << "\n";
        std::cout << std::endl;
    
        gpuErrchk(cudaFree(d_data));
        gpuErrchk(cudaFree(d_result));
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      • 2015-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多