1

次の F# コードは、3 回目の呼び出しで no memory 例外でクラッシュします。何かが足りないか、何らかの理由で Alea がメモリを正しく解放していません。F# Interactive と Compiled の両方で試しました。Dispose を手動で呼び出してみましたが、うまくいきませんでした。理由はありますか?

let squareGPU (inputs:float[]) =
        use dInputs = worker.Malloc(inputs)
        use dOutputs = worker.Malloc(inputs.Length)
        let blockSize = 256
        let numSm = worker.Device.Attributes.MULTIPROCESSOR_COUNT
        let gridSize = Math.Min(16 * numSm, divup inputs.Length blockSize)
        let lp = new LaunchParam(gridSize, blockSize)
        worker.Launch <@ squareKernel @> lp dOutputs.Ptr dInputs.Ptr inputs.Length
        dOutputs.Gather()


let x = squareGPU [|0.0..0.001..100000.0|]
printfn "1" 
let y = squareGPU [|0.0..0.001..100000.0|]
printfn "2" 
let z = squareGPU [|0.0..0.001..100000.0|]
printfn "3"
4

2 に答える 2

0

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 配列を提供し、デバイスからのデータで埋められるようにします。

于 2015-08-01T14:14:24.027 に答える