Swift, macOS, ma c с 2 графическими процессорами, матричные операции работают на одном графическом процессоре, а не на другом - PullRequest
2 голосов
/ 08 апреля 2020

Я использую macOS на своем MacBook Pro (Retina, 15-дюймовый, середина 2015 г.), в котором есть два графических процессора, в соответствии с пунктом «Об этом Ма c» в меню Apple. Один графический процессор - AMD Radeon R9 M370X 2 ГБ, другой - Intel Iris Pro 1536 МБ - стандартные чипы, я полагаю? Это чипы, которые были там, когда я его купил, я ничего себе не добавил.

Я использую библиотеку Swift MPS для матричных вычислений; он отлично работает на графическом процессоре Intel, но когда я выбираю Radeon, я получаю только нули обратно от каждой операции, без сообщений об ошибках. Я искал документацию, но ничего не могу найти. Единственная подсказка, которую я имею до сих пор, заключается в том, что Radeon сообщает, что он «не интегрирован» (или, по крайней мере, я думаю, что так и есть, основываясь на примере кода на Поиск графических процессоров на macOS , что примерно так же полезно, как на Apple do c когда-либо есть, что означает не очень ). Если я правильно прочитал эту страницу, это то, что мне говорят мои два графических процессора.

Device Intel Iris Pro Graphics; caps: headful, not discrete, integrated, not external

Device AMD Radeon R9 M370X; caps: headful, discrete, not integrated, not external

Я не могу найти ничего c это подсказало бы, что я делаю неправильно. Я проверил документацию Apple MPS, но безрезультатно. И, как я уже сказал, код прекрасно работает на Intel GPU, поэтому я думаю, что он будет работать и на Radeon. Я запустил некоторые загружаемые инструменты диагностики c, чтобы проверить Radeon, но он не отображается в меню этих инструментов. Поэтому я даже не знаю, что я делаю неправильно в коде, или сам чип сломан.

Ниже приведен код, который вы можете создать как консольное приложение, вставив в него код. main.swift. Найдите следующую строку:

let device = MTLCopyAllDevices()[1]

Я использую [0] для Intel, [1] для Radeon, и вы можете видеть, что вывод отличается, то есть все нули для Radeon. Я полагаю, ваш пробег может варьироваться в зависимости от вашей машины. Я приветствую любой вклад, ура

import MetalPerformanceShaders

typealias MPSNumber = Float32

let MPSNumberSize = MemoryLayout<MPSNumber>.size
let MPSNumberTypeInGPU = MPSDataType.float32

class MPSNet {
    let commandBuffer: MTLCommandBuffer
    let commandQueue: MTLCommandQueue
    let device = MTLCopyAllDevices()[1]
    var neuronsInMatrix1: MPSMatrix?
    var neuronsInMatrix2: MPSMatrix?
    var neuronsOutMatrix: MPSMatrix?

    init() {
        guard let cq = device.makeCommandQueue() else { fatalError() }
        guard let cb = cq.makeCommandBuffer() else { fatalError() }

        commandQueue = cq
        commandBuffer = cb

        let cMatrices = 2
        let cRows = 1
        let cColumns = 3

        let sensoryInputs1: [MPSNumber] = [1, 2, 3]
        let sensoryInputs2: [MPSNumber] = [4, 5, 6]

        neuronsInMatrix1 = makeMatrix(device, sensoryInputs1)
        neuronsInMatrix2 = makeMatrix(device, sensoryInputs2)

        let rowStride = MPSMatrixDescriptor.rowBytes(fromColumns: cColumns, dataType: MPSNumberTypeInGPU)
        neuronsOutMatrix = makeMatrix(device, cRows, cColumnsOut: cColumns, rowStride: rowStride)

        let adder = MPSMatrixSum(
            device: device, count: cMatrices, rows: cRows, columns: cColumns, transpose: false
        )

        adder.encode(
            to: commandBuffer,
            sourceMatrices: [neuronsInMatrix1!, neuronsInMatrix2!],
            resultMatrix: neuronsOutMatrix!, scale: nil, offsetVector: nil,
            biasVector: nil, start: 0
        )

        commandBuffer.addCompletedHandler { _ in
            let motorOutputs = self.getComputeOutput(self.neuronsOutMatrix!)

            let discrete = !self.device.isLowPower && !self.device.isRemovable
            let caps = "\(self.device.isHeadless ? " headless" : " headful")" +
                       "\(discrete ? ", discrete" : ", not discrete")" +
                       "\(self.device.isLowPower ? ", integrated" : ", not integrated")" +
                       "\(self.device.isRemovable ? ", external" : ", not external")"

            print("Device \(self.device.name); caps:\(caps); motor outputs \(motorOutputs)")
        }
    }

    func compute() {
        commandBuffer.commit()
        commandBuffer.waitUntilCompleted()
    }
}

extension MPSNet {
    func getComputeOutput(_ matrix: MPSMatrix) -> [Double] {
        let rc = matrix.data.contents()
        return stride(from: 0, to: matrix.columns * MPSNumberSize, by: MPSNumberSize).map {
            offset in

            let rr = rc.load(fromByteOffset: offset, as: MPSNumber.self)

            return Double(rr)
        }
    }

    func loadMatrix(_ data: MTLBuffer, _ rawValues: [MPSNumber]) {
        let dContents = data.contents()

        zip(stride(from: 0, to: rawValues.count * MPSNumberSize, by: MPSNumberSize), rawValues).forEach { z in
            let (byteOffset, rawValue) = (z.0, MPSNumber(z.1))

            dContents.storeBytes(of: rawValue, toByteOffset: byteOffset, as: MPSNumber.self)
        }
    }

    func makeMatrix(_ device: MTLDevice, _ rawValues: [MPSNumber]) -> MPSMatrix {
        let rowStride = MPSMatrixDescriptor.rowBytes(
            fromColumns: rawValues.count, dataType: MPSNumberTypeInGPU
        )

        let descriptor = MPSMatrixDescriptor(
            dimensions: 1, columns: rawValues.count, rowBytes: rowStride,
            dataType: MPSNumberTypeInGPU
        )

        guard let inputBuffer = device.makeBuffer(
            length: descriptor.matrixBytes, options: MTLResourceOptions.storageModeManaged
        ) else { fatalError() }

        loadMatrix(inputBuffer, rawValues)

        return MPSMatrix(buffer: inputBuffer, descriptor: descriptor)
    }

    func makeMatrix(_ device: MTLDevice, _ cRowsOut: Int, cColumnsOut: Int, rowStride: Int) -> MPSMatrix {
        let matrixDescriptor = MPSMatrixDescriptor(
            dimensions: cRowsOut, columns: cColumnsOut,
            rowBytes: rowStride, dataType: MPSNumberTypeInGPU
        )

        return MPSMatrix(device: device, descriptor: matrixDescriptor)
    }
}

let net = MPSNet()
net.compute()

1 Ответ

0 голосов
/ 30 апреля 2020

Похоже, вы не смогли использовать - [MPSMatrix synchronizeOnCommandBuffer:]. На дискретных устройствах требуется некоторая явная синхронизация, прежде чем данные будут возвращены из графического процессора.

...