【发布时间】:2020-07-20 07:04:00
【问题描述】:
根据 Apple 菜单中的“关于本机”,我在我的 MacBook Pro(Retina,15 英寸,2015 年中)上运行 macOS,其中有两个 GPU。一个 GPU 是 AMD Radeon R9 M370X 2 GB,另一个是 Intel Iris Pro 1536 MB——标准芯片,我猜?它们是我买的时候里面的芯片,我自己没有添加。
我正在使用 Swift MPS 库进行矩阵计算;它在 Intel GPU 上运行良好,但是当我选择 Radeon 时,我只会从每个操作中返回零,没有报告错误。我四处寻找有关它的文档,但找不到任何东西。到目前为止,我唯一的线索是 Radeon 报告“未集成”(或者至少,我认为它确实如此,基于 Finding GPUs on macOS 的示例代码,这与 Apple 的文档一样有用,这意味着 不是很)。如果我没看错那页,这就是我的两个 GPU 告诉我的。
Device Intel Iris Pro Graphics; caps: headful, not discrete, integrated, not external
Device AMD Radeon R9 M370X; caps: headful, discrete, not integrated, not external
我找不到任何可以表明我做错了什么的文档。我翻遍了 Apple 的 MPS 文档,但无济于事。正如我所说,代码在 Intel GPU 上运行良好,所以我认为它也可以在 Radeon 上运行。我已经运行了一些可下载的诊断工具来检查 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()
【问题讨论】:
-
问题:在 Windows 上,当有多个可用 gpu 时,您必须分配一个应用程序/程序以在特定 gpu 上运行 - 或者您可以根据您的电源状态让所有内容在其中一个上运行(即 - 插入或电池)。从我在我的 Mac 上可以看出 - 我可以做出基于电源状态的选择。也许您可以尝试一下,告诉我当您在插入 AMD 时让所有东西都运行时会发生什么,然后我们可以检查是否可以进行基于应用程序的设置(如果您对此感兴趣)
-
嘿,非常感谢!仅仅通过跟进你的领导,我学到的知识比我以前知道的要多一百倍。我尝试了该电源设置,并阅读了一些与之相关的文章,现在我开始认为芯片坏了。如果你愿意,我会及时通知你,这样我们就可以整理出一个答案,当我最终弄清楚一些事情时,你可以得到分数。干杯
-
啊,芯片坏了?太糟糕了,MacBook Pro 上的一切都那么紧凑。我通常可以通过将热风枪对准主板上的 gpu 轻松验证芯片是否已损坏或芯片座是否由于过热而退化。如果它在接下来的 10 分钟内起作用并且失败了 - 是微球融化并再次移位和移位。但在你走这条疯狂的科学家/破产玩家路线之前,请询问具有类似工作环境的人,看看代码是否能在他们身上工作。
-
我猜芯片没问题;我从 Apple 下载了一些用 Objective-C 编写的东西,我真的不知道,但我设法编译它并使用两个 GPU 很好地看到它。今天我会试着做更多的工作,以找出细节。干杯
-
@SamThomas 问题是我使用的是
MTLResourceOptions.storageModeManaged而不是MTLResourceOptions.storageModeShared。我改变了这一点,即使没有调用MPSMatrix.synchronize(on:),一切正常,如下面的答案之一所述。祝你好运
标签: swift macos gpu matrix-multiplication