newComputePipelineStateWithFunction 失败
newComputePipelineStateWithFunction failed
我正在尝试让神经网络 运行 在金属上。
基本思想是数据复制。每个 gpu 线程 运行s 一个版本的网络用于随机数据点。
我编写了其他工作正常的着色器。
我还在 C++ 命令行应用程序中尝试了我的代码。那里没有错误。
也没有编译错误。
我使用 Apple 文档转换为 Metal C++,因为并非所有来自 C++11 的内容都受支持。
它在加载内核函数后以及尝试将 newComputePipelineStateWithFunction
分配给金属设备时崩溃。这意味着编译时未捕获的代码存在问题。
MCVE:
kernel void net(const device float *inputsVector [[ buffer(0) ]], // layout of net *
uint id [[ thread_position_in_grid ]]) {
uint floatSize = sizeof(tempFloat);
uint inputsVectorSize = sizeof(inputsVector) / floatSize;
float newArray[inputsVectorSize];
float test = inputsVector[id];
newArray[id] = test;
}
更新
它与动态数组有关。
由于它无法创建管线状态并且不会崩溃 运行实际着色器它一定是编码问题。不是输入问题。
将动态数组中的值赋给缓冲区会导致失败。
我认为你的问题出在这一行:
uint schemeVectorSize = sizeof(schemeVector) / uintSize;
这里的 schemeVector
是 dynamic 所以 as in classic C++ 你不能在动态数组上使用 sizeof
来获取元素的数量。 sizeof
仅适用于您在金属着色器代码中定义 locally/statically 的数组。
想象一下它在内部是如何工作的:在编译时,Metal 编译器应该将 sizeof
调用转换为常量……但他不能,因为 schemeVector
是一个参数你的着色器,因此可以有任何大小...
所以对我来说,解决方案是在代码的 C++/ObjectiveC/Swift 部分计算 schemeVectorSize
,并将其作为参数传递给着色器(作为 OpenGLES 术语中的统一标准...... .).
真正的问题:
内存问题!
对于所有说这是内存问题的人,您是对的!
这是一些伪代码来说明它。抱歉,它在 "Swift" 中,但更易于阅读。金属着色器有一种时髦的方式来生活。它们首先在没有值的情况下初始化以获取内存。正是这一步失败了,因为它依赖于后面的一步:设置缓冲区。
这一切都归结为哪些值在何时可用。我对 newComputePipelineStateWithFunction
的理解是错误的。它不是简单地获取着色器功能。这也是初始化过程中的一小步。
class MetalShader {
// buffers
var aBuffer : [Float]
var aBufferCount : Int
// step One : newComputePipelineStateWithFunction
memory init() {
// assign shader memory
// create memory for one int
let aStaticValue : Int
// create memory for one int
var aNotSoStaticValue : Int // this wil succeed, assigns memory for one int
// create memory for 10 floats
var aStaticArray : [Float] = [Float](count: aStaticValue, repeatedValue: y) // this will succeed
// create memory for x floats
var aDynamicArray : [Float] = [Float](count: aBuffer.count, repeatedValue: y) // this will fail
var aDynamicArray : [Float] = [Float](count: aBufferCount, repeatedValue: y) // this will fail
let tempValue : Float // one float from a loop
}
// step Two : commandEncoder.setBuffer()
assign buffers (buffers) {
aBuffer = cpuMemoryBuffer
}
// step Three : commandEncoder.endEncoding()
actual init() {
// set shader values
let aStaticValue : Int = 0
var aNotSoStaticValue : Int = aBuffer.count
var aDynamicArray : [Float] = [Float](count: aBuffer.count, repeatedValue: 1) // this could work, but the app already crashed before getting to this point.
}
// step Four : commandBuffer.commit()
func shaderFunction() {
// do stuff
for i in 0..<aBuffer.count {
let tempValue = aBuffer[i]
}
}
}
修复:
我终于意识到缓冲区在技术上是动态数组,而不是在着色器中创建数组,我也可以只添加更多缓冲区。这显然有效。
我正在尝试让神经网络 运行 在金属上。 基本思想是数据复制。每个 gpu 线程 运行s 一个版本的网络用于随机数据点。
我编写了其他工作正常的着色器。
我还在 C++ 命令行应用程序中尝试了我的代码。那里没有错误。 也没有编译错误。
我使用 Apple 文档转换为 Metal C++,因为并非所有来自 C++11 的内容都受支持。
它在加载内核函数后以及尝试将 newComputePipelineStateWithFunction
分配给金属设备时崩溃。这意味着编译时未捕获的代码存在问题。
MCVE:
kernel void net(const device float *inputsVector [[ buffer(0) ]], // layout of net *
uint id [[ thread_position_in_grid ]]) {
uint floatSize = sizeof(tempFloat);
uint inputsVectorSize = sizeof(inputsVector) / floatSize;
float newArray[inputsVectorSize];
float test = inputsVector[id];
newArray[id] = test;
}
更新
它与动态数组有关。
由于它无法创建管线状态并且不会崩溃 运行实际着色器它一定是编码问题。不是输入问题。
将动态数组中的值赋给缓冲区会导致失败。
我认为你的问题出在这一行:
uint schemeVectorSize = sizeof(schemeVector) / uintSize;
这里的 schemeVector
是 dynamic 所以 as in classic C++ 你不能在动态数组上使用 sizeof
来获取元素的数量。 sizeof
仅适用于您在金属着色器代码中定义 locally/statically 的数组。
想象一下它在内部是如何工作的:在编译时,Metal 编译器应该将 sizeof
调用转换为常量……但他不能,因为 schemeVector
是一个参数你的着色器,因此可以有任何大小...
所以对我来说,解决方案是在代码的 C++/ObjectiveC/Swift 部分计算 schemeVectorSize
,并将其作为参数传递给着色器(作为 OpenGLES 术语中的统一标准...... .).
真正的问题: 内存问题!
对于所有说这是内存问题的人,您是对的! 这是一些伪代码来说明它。抱歉,它在 "Swift" 中,但更易于阅读。金属着色器有一种时髦的方式来生活。它们首先在没有值的情况下初始化以获取内存。正是这一步失败了,因为它依赖于后面的一步:设置缓冲区。
这一切都归结为哪些值在何时可用。我对 newComputePipelineStateWithFunction
的理解是错误的。它不是简单地获取着色器功能。这也是初始化过程中的一小步。
class MetalShader {
// buffers
var aBuffer : [Float]
var aBufferCount : Int
// step One : newComputePipelineStateWithFunction
memory init() {
// assign shader memory
// create memory for one int
let aStaticValue : Int
// create memory for one int
var aNotSoStaticValue : Int // this wil succeed, assigns memory for one int
// create memory for 10 floats
var aStaticArray : [Float] = [Float](count: aStaticValue, repeatedValue: y) // this will succeed
// create memory for x floats
var aDynamicArray : [Float] = [Float](count: aBuffer.count, repeatedValue: y) // this will fail
var aDynamicArray : [Float] = [Float](count: aBufferCount, repeatedValue: y) // this will fail
let tempValue : Float // one float from a loop
}
// step Two : commandEncoder.setBuffer()
assign buffers (buffers) {
aBuffer = cpuMemoryBuffer
}
// step Three : commandEncoder.endEncoding()
actual init() {
// set shader values
let aStaticValue : Int = 0
var aNotSoStaticValue : Int = aBuffer.count
var aDynamicArray : [Float] = [Float](count: aBuffer.count, repeatedValue: 1) // this could work, but the app already crashed before getting to this point.
}
// step Four : commandBuffer.commit()
func shaderFunction() {
// do stuff
for i in 0..<aBuffer.count {
let tempValue = aBuffer[i]
}
}
}
修复:
我终于意识到缓冲区在技术上是动态数组,而不是在着色器中创建数组,我也可以只添加更多缓冲区。这显然有效。