GC は別のバックグラウンド スレッドで動作するため、新しい巨大な配列を頻繁に作成すると、メモリ不足の例外が簡単にスローされます。
この大きな配列の場合、より安定した「インプレース変更」スタイルを使用することをお勧めします。それを示すテストを作成しました: (注: 配列が非常に大きいため、プロジェクトのプロパティ ページに移動し、[ビルド] タブで [32 ビットを優先] のチェックを外して、64 ビットで実行されることを確認します。処理する)
open System
open Alea.CUDA
open Alea.CUDA.Utilities
open NUnit.Framework
[<ReflectedDefinition>]
let squareKernel (outputs:deviceptr<float>) (inputs:deviceptr<float>) (n:int) =
let start = blockIdx.x * blockDim.x + threadIdx.x
let stride = gridDim.x * blockDim.x
let mutable i = start
while i < n do
outputs.[i] <- inputs.[i] * inputs.[i]
i <- i + stride
let squareGPUInplaceUpdate (worker:Worker) (lp:LaunchParam) (hData:float[]) (dData:DeviceMemory<float>) =
// instead of malloc a new device memory, you just reuse the device memory dData
// and scatter new data to it.
dData.Scatter(hData)
worker.Launch <@ squareKernel @> lp dData.Ptr dData.Ptr hData.Length
// actually, there should be a counterpart of data.Scatter(hData) like data.Gather(hData)
// but unfortunately, that is missing, but there is a workaround of using worker.Gather.
worker.Gather(dData.Ptr, hData)
let squareGPUManyTimes (iters:int) =
let worker = Worker.Default
// actually during the many iters, you just malloc 2 host array (for data and expected value)
// and you malloc a device array. You keep reusing them, since they are big array.
// if you new the huge array very frequentely, GC is under pressure. and since GC works
// as a separate thread, so you will get System.OutOfMemoryException from time to time.
let hData = [|0.0..0.001..100000.0|]
let n = hData.Length
let expected = Array.zeroCreate n
use dData = worker.Malloc<float>(n)
let rng = Random()
let update () =
// in-place updating the data
for i = 0 to n - 1 do
hData.[i] <- rng.NextDouble()
expected.[i] <- hData.[i] * hData.[i]
let lp =
let blockSize = 256
let numSm = worker.Device.Attributes.MULTIPROCESSOR_COUNT
let gridSize = Math.Min(16 * numSm, divup n blockSize)
new LaunchParam(gridSize, blockSize)
for i = 1 to iters do
update()
squareGPUInplaceUpdate worker lp hData dData
Assert.AreEqual(expected, hData)
printfn "iter %d passed..." i
[<Test>]
let test() =
squareGPUManyTimes 5
例外は常にホスト メモリを意味することに注意してくださいSystem.OutOfMemoryException
。十分なメモリがない場合、GPU メモリは CUDAException をスローします。
ところで、DeviceMemory.Gather() を呼び出すたびに、新しい .NET 配列が作成され、それが埋められます。この例に示されているインプレース メソッドを使用して、.net 配列を提供し、デバイスからのデータで埋められるようにします。