c# - 将内核链接在一起时值随机变化

标签 c# c++ opencl gpgpu opencl.net

我对整个 OpenCL 世界还很陌生,我创建了两个非常简单的内核,并试图将它们链接在一起,但我得到了相当虚假的结果。当单独运行时,它们按预期工作,但是当一起运行时,我就会看到奇怪的结果。

所以,每个内核看起来像这样

vector 3 噪声

__kernel void addVector3Noise( __global struct State* states, __global float3* randomVector3Values){
    int stateNum = get_global_id(0);
    struct State state = states[stateNum];
    float3 randomVal = randomVector3Values[stateNum];
    struct State newState;
    newState.Vec3 = (float3)(state.Vec3.x + randomVal.x,state.Vec3.y + randomVal.y,state.Vec3.z + randomVal.z); 
    newState.Vec4 = state.Vec4;
    states[stateNum] = newState;
}

为了测试这一点,所有状态的 Vec3 均为 [ 1.0f, 1.0f, 1.0f],并且所有随机值都相同,因此我得到的输出是一个状态数组,其值为 [2.0f, 2.0f, 2.0f] 正如我所期望的。

vector 4 噪声

__kernel void addVector4Noise(__global struct State* states,
__global float3* randomVector4Values){
    int stateNum = get_global_id(0);
    struct State state = states[stateNum];
    float3 randomVal = randomVector4Values[stateNum];
    float4 newVector4 = randomQuaternionRotation(state.Vector4, randomVal);
    struct State newState;
    newState.Vector3 = state.Vector3;
    newState.Vector4= newVector4;
    states[stateNum] = newState;    
}

使用非常简单的测试数据运行它也给了我我想要的。

现在,当将它们链接在一起时,问题就出现了。我按照 vector 4 噪声 -> vector 3 噪声的顺序调用它们。现在,当运行 Vector 4 噪声内核时,我看到 vector 3 值发生变化,并且该变化似乎遵循某种模式。

因此,在运行 vector 4 内核后,我希望每个状态下的 vector 3 与插入时相同。因此,这意味着每个状态的 vector 3 值为 [1.0f,1.0f,1.0f] 以下是我实际看到的 vector 3 的结果:

[1.0,1.0,1.0] 
[0.576367259,1.0,1.0]
[0.999199867,0.6448302,1.0]
[1.313311, 1.067663, 0.3307195]
[-0.08005857, 1.067663, 1.450237]
[1, 0.2340522, 1.136126]
[1, 1, 0.3025152]
[1, 1, 1]

并且该模式在所有 Vector 3 值中重复出现。请注意,在内核中,它只是将 Vector3 从先前的状态复制到新的状态。

这就是我使用 OpenCL.Net 将它们链接在一起的方式

    using (var env = "*".CreateCLEnvironment(DeviceType.Gpu))
    {           
        var source = LoadProgram("kernels.cl");
        var context = env.Context;

        ErrorCode errorCode;
        var program = Cl.CreateProgramWithSource(context, 1u, source, null, out errorCode);
        CheckSuccess(errorCode);
        errorCode = Cl.BuildProgram(program, (uint)env.Devices.Length, env.Devices, "-cl-opt-disable", null,
            IntPtr.Zero);
        if (errorCode != ErrorCode.Success)
        {
            var info = Cl.GetProgramBuildInfo(program, env.Devices[0], ProgramBuildInfo.Log, out errorCode).ToString();
            throw new Exception(info);
        }

        var kernels = Cl.CreateKernelsInProgram(program, out errorCode);
        CheckSuccess(errorCode);
        var Vector4NoiseKernel = kernels[0];
        var Vector3NoiseKernel = kernels[1];

        var rnd = new Random();
        var states = Enumerable.Range(1, ArrayLength)
            .Select(_ => new State
            {
                Vector3 = new Vector3(1, 1, 1),
                Vector4 = new Vector4(0.5f,0.5f,0.5f,0.5f)
            })
            .ToArray();
        var randomVector4Values = Enumerable.Range(1, ArrayLength)
            .Select(_ => new Vector3(2f, 2f, 2f))
            .ToArray();

        var randomVector3Values = Enumerable.Range(1, ArrayLength)
            .Select(_ => new Vector3(1f, 1f, 1f))
            .ToArray();

        var vector4StatesBuffer = context.CreateBuffer(states, MemFlags.ReadWrite);
        var randomVector4ValuesBuffer = context.CreateBuffer(randomVector4Values, MemFlags.ReadOnly);

        Event ev;

        Cl.SetKernelArg(vector4NoiseKernel, 0, vector4StatesBuffer);
        Cl.SetKernelArg(vector4NoiseKernel, 1, randomVector4ValuesBuffer);

        errorCode = Cl.EnqueueNDRangeKernel(env.CommandQueues[0], vector4NoiseKernel, 1, null
            , new[] { new IntPtr(ArrayLength) }, new[] { new IntPtr(1) }, 0u, null, out ev);
        errorCode.Check();

        env.CommandQueues[0].ReadFromBuffer(vector4StatesBuffer, states, waitFor: ev);

        var randomVector3ValuesBuffer = context.CreateBuffer(randomVector3Values, MemFlags.ReadOnly);
        var vector3StatesBuffer = context.CreateBuffer(states, MemFlags.ReadWrite);


        Cl.SetKernelArg(vector3NoiseKernel, 0, vector3StatesBuffer);
        Cl.SetKernelArg(vector3NoiseKernel, 1, randomVector3ValuesBuffer);

        errorCode = Cl.EnqueueNDRangeKernel(env.CommandQueues[0], vector3NoiseKernel, 1, null
            , new[] { new IntPtr(ArrayLength) }, new[] { new IntPtr(1) }, 0u, null, out ev);
        errorCode.Check();

        Cl.Finish(env.CommandQueues[0]).Check();
        env.CommandQueues[0].ReadFromBuffer(vector3StatesBuffer, states, waitFor: ev);
    }

请原谅那里的大量代码,但这是一个 Playground 项目,我几乎只是吐出想法,所以整洁和优雅在这里不是问题:)

预先感谢您提供的任何帮助。

编辑 因此,我今天早上做的第一件事就是将每个内核拉出到自己的 cl 文件中,并确保每个内核都有自己的状态版本,仅包含其所需的内容(分别为 Vector4 和 Vector3),以及一个新的 using 语句与新分离出的 Vector3 噪声内核一起使用的所有 gubbin。令我高兴的是,Vector4 噪声内核的表现完全符合我的预期,但是,当涉及到 Vector3 噪声时,出现了与之前类似的问题。仍然传递 [1.0f,1.0f,1.0f] 作为随机值和起始 Vector3 值,但它仍然没有产生我期望的输出。这次重复的模式是:

[2.0f,2.0f,2.0f]
[1.0f,2.0f,2.0f]
[2.0f,1.0f,2.0f]
[2.0f,2.0f,1.0f]
[2.0f,2.0f,2.0f]

最佳答案

在 OpenCL 中,3 分量 vector 类型与 4 分量 vector 类型占用相同的大小。例如,float3 被定义为 16 字节,而不是 12 字节。如果您在主机上使用的数据结构(本例中为 Vector3 类)大小不同,您可能会遇到问题。

您编辑的帖子中的输出模式是三个 2.0,然后是 1.0,这表明这可能是您所看到的行为的原因。

一种解决方案是在主机端使用 Vector4 代替 Vector3

关于c# - 将内核链接在一起时值随机变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32954410/

相关文章:

c# - 如何使用Javascript检查Gridview模板中的复选框是否被选中?

c++ - 什么是 NULL 值

c++ - 如何使用 OpenMP 并行化此 Matrix times Vector 操作?

opencl - OpenCL 上内存传输和内核执行可以同时运行吗

opencl - 需要为 CPU 和 GPU 平台安装 opencl?

c# - 事件处理程序或回调

c# - 如何在等待 ReadLine 时更新 C# Windows 控制台应用程序中的当前行?

C++ 模板 : how to determine if a type is suitable for subclassing

sdk - OpenCL/AMD : Deep Learning

c# - 条件变量值