忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

GPUで疑似乱数 (OpenCL, Cloo)(Xorshift)

GPUで擬似乱数

DirectX11を使ってGPUで擬似乱数を生成したので、今度はOpenCLで同じことをやってみます。

Program.cs

using System.Linq;
using Cloo;

struct Xorshift128RandomGpu
{
    public int w, x, y, z;

    public Xorshift128RandomGpu(int seed)
    {
        if (seed == 0)
        {
            seed += 11;
        }

        w = seed;
        x = seed << 16 + seed >> 16;
        y = w + x;
        z = x ^ y;
    }
}

class Program
{
    static void Main()
    {
        var platform = ComputePlatform.Platforms
            .Where(p => p.Devices.Count(d => d.Type == ComputeDeviceTypes.Gpu) > 0)
            .First();
        var devices = platform.Devices
            .Where(d => d.Type == ComputeDeviceTypes.Gpu)
            .ToArray();
        var context = new ComputeContext(
            devices, 
            new ComputeContextPropertyList(platform),
            null,
            System.IntPtr.Zero
            );
        var commandQueue = new ComputeCommandQueue(
            context,
            devices[0], 
            ComputeCommandQueueFlags.None
            );
        const int elementCount = 20;
        var resultBuffer = new ComputeBuffer<float>(
            context, 
            ComputeMemoryFlags.ReadWrite,
            elementCount
            );
        var random = new System.Random(0);
        var randomGeneratorsBuffer = new ComputeBuffer<Xorshift128RandomGpu>(
            context,
            ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.CopyHostPointer,
            Enumerable.Range(0, elementCount).Select(i=>new Xorshift128RandomGpu(random.Next())).ToArray()
            );
        var program = new ComputeProgram(
            context,
            System.IO.File.ReadAllText("myKernelProgram.cl")
            );
        try
        {
            program.Build(devices, null, null, System.IntPtr.Zero);
        }
        catch
        {
            System.Console.WriteLine(program.GetBuildLog(devices[0]));
        }
        var kernel = program.CreateKernel("updateRandom");
        kernel.SetMemoryArgument(0, resultBuffer);
        kernel.SetMemoryArgument(1, randomGeneratorsBuffer);
        commandQueue.Execute(
            kernel,
            null,
            new long[] { elementCount },
            new long[] { 1 },
            null
            );

        float[] dataFromGpu = new float[elementCount];
        commandQueue.ReadFromBuffer<float>(resultBuffer, ref dataFromGpu, true, null);

        foreach (var number in dataFromGpu)
        {
            System.Console.WriteLine(number);
        }

        kernel.Dispose();
        program.Dispose();
        randomGeneratorsBuffer.Dispose();
        resultBuffer.Dispose();
        commandQueue.Dispose();
        context.Dispose();
    }
}

myKernelProgram.cl

typedef struct
{
	int w;
	int x;
	int y;
	int z;	
}Xorshift128Random;


int next(Xorshift128Random* random)
{
	int t = (random->x ^ (random->x << 11));
	random->x = random->y;
	random->y = random->z;
	random->z = random->w;
	random->w = (random->w = (random->w ^ (random->w >> 19)) ^ (t ^ (t >> 8)));
	return random->w;
}

float nextFloat(Xorshift128Random* random)
{
	return ((float)next(random) / INT_MAX);
}

__kernel void updateRandom(
	__global float *resultBuffer,
	__global Xorshift128Random *randomGeneratorBuffer)
{
	Xorshift128Random random = randomGeneratorBuffer[get_global_id(0)];
	resultBuffer[get_global_id(0)] = nextFloat(&random);
	randomGeneratorBuffer[get_global_id(0)] = random;
}

結果は次のようになります:

0.7322494
0.8232476
0.7558671
0.5355725
0.2103059
0.5624865
0.8987527
0.4662975
0.998933
0.2610296
0.2951466
0.4376118
0.6250286
0.4811476
0.9961796
0.01847547
0.8486539
0.9736332
0.671631
0.3234635

この前と結果は同じですね。
seedが同じなので当たり前ですが。
上手く行っているようです。

拍手[0回]

PR