我的iOS金属计算内核是否存在编译器错误,或者我遗漏了什么
Is there a compiler bug for my iOS metal compute kernel or am I missing something?
我需要一个upper_bound
的实现,如我的金属计算内核的STL中所述。在金属标准库中没有任何东西,我基本上将它从<algorithm>
复制到我的着色器文件中,如下所示:
static device float* upper_bound( device float* first, device float* last, float val)
{
ptrdiff_t count = last - first;
while( count > 0){
device float* it = first;
ptrdiff_t step = count/2;
it += step;
if( !(val < *it)){
first = ++it;
count -= step + 1;
}else count = step;
}
return first;
}
我创建了一个简单的内核来测试它:
kernel void upper_bound_test(
device float* input [[buffer(0)]],
device uint* output [[buffer(1)]]
)
{
device float* where = upper_bound( input, input + 5, 3.1);
output[0] = where - input;
}
该测试具有硬编码的输入大小和搜索值。我还在框架端硬编码了一个5元素的输入缓冲区,如下所示。这个内核我期望返回的第一个输入的索引大于3.1
它不起作用。事实上,output[0]
从来没有被写过——因为我用一个神奇的数字预加载了缓冲区,看看它是否被重写了。事实并非如此。事实上,在waitUntilCompleted
之后,commandBuffer.error
看起来是这样的:
Error Domain = MTLCommandBufferErrorDomain
Code = 1
NSLocalizedDescription = "IOAcceleratorFamily returned error code 3"
错误代码3是什么意思?我的内核还没来得及完成就被杀了吗?
此外,我只尝试了upper_bound
的线性搜索版本,比如
static device float* upper_bound2( device float* first, device float* last, float val)
{
while( first < last && *first <= val)
++first;
return first;
}
这个(有点)管用。我对<algorithm>
的二进制搜索下界也有同样的问题——然而一个简单的线性版本(有点)有效。顺便说一句,我测试了我从直接C代码中复制的STL版本(显然去掉了device
),它们在着色器领域之外工作得很好。请告诉我我做错了什么,这不是一个金属编译器错误。
现在关于上面的"排序":线性搜索版本适用于5s和mini-2(A7s)(在上例中返回索引3),但在6+(A8)上它给出了正确的答案+2^31。见鬼!完全相同的代码。请注意,在框架方面,我使用uint32_t
,在着色器方面使用uint
,这是相同的。还要注意,每个指针减法(ptrdiff_t
是有符号的8字节内容)都是小的非负值。为什么6+设置的是高位?当然,为什么我真正的二进制搜索版本不起作用呢?
这是框架方面的东西:
id<MTLFunction> upperBoundTestKernel = [_library newFunctionWithName: @"upper_bound_test"];
id <MTLComputePipelineState> upperBoundTestPipelineState = [_device
newComputePipelineStateWithFunction: upperBoundTestKernel
error: &err];
float sortedNumbers[] = {1., 2., 3., 4., 5.};
id<MTLBuffer> testInputBuffer = [_device
newBufferWithBytes:(const void *)sortedNumbers
length: sizeof(sortedNumbers)
options: MTLResourceCPUCacheModeDefaultCache];
id<MTLBuffer> testOutputBuffer = [_device
newBufferWithLength: sizeof(uint32_t)
options: MTLResourceCPUCacheModeDefaultCache];
*(uint32_t*)testOutputBuffer.contents = 42;//magic number better get clobbered
id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
id<MTLComputeCommandEncoder> commandEncoder = [commandBuffer computeCommandEncoder];
[commandEncoder setComputePipelineState: upperBoundTestPipelineState];
[commandEncoder setBuffer: testInputBuffer offset: 0 atIndex: 0];
[commandEncoder setBuffer: testOutputBuffer offset: 0 atIndex: 1];
[commandEncoder
dispatchThreadgroups: MTLSizeMake( 1, 1, 1)
threadsPerThreadgroup: MTLSizeMake( 1, 1, 1)];
[commandEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
uint32_t answer = *(uint32_t*)testOutputBuffer.contents;
好吧,我找到了一个解决方案。我猜想这是一个指针混叠问题,因为first
和last
指向同一个缓冲区。所以我将它们更改为单个指针变量的偏移量。这是一个重写的upper_bound2:
static uint upper_bound2( device float* input, uint first, uint last, float val)
{
while( first < last && input[first] <= val)
++first;
return first;
}
以及一个重新编写的测试内核:
kernel void upper_bound_test(
device float* input [[buffer(0)]],
device uint* output [[buffer(1)]]
)
{
output[0] = upper_bound2( input, 0, 5, 3.1);
}
这完全奏效了。也就是说,它不仅解决了线性搜索的"某种"问题,而且类似的重写二进制搜索也起到了作用。不过我不想相信。金属着色器语言应该是C++的一个子集,但标准指针语义不起作用?我真的可以不比较或减去指针吗?
无论如何,我不记得看到任何文档说不可能有指针别名,或者什么声明咒语会在这里帮助我。还有什么帮助吗?
[更新]
记录在案,正如苹果开发者论坛上的"slime"所指出的:https://developer.apple.com/library/ios/documentation/Metal/Reference/MetalShadingLanguageGuide/func-var-qual/func-var-qual.html#//apple_ref/doc/uid/TP40014364-ch4sw3
指定为图形或内核函数参数值的缓冲区(设备和常量)不能有别名,也就是说,作为参数值传递的缓冲区不能与传递给同一图形或内核功能的单独参数的另一个缓冲区重叠
但也值得注意的是,upper_bound()不是一个内核函数,upper_ound_test()也没有传递别名参数。upper_bound_test()所做的是创建一个本地临时,该临时指向与其参数之一相同的缓冲区。也许文档应该说明它的含义,比如:"在任何函数中,包括右值在内,都不允许对设备和常量内存进行指针别名。"我真的不知道这是否太强了。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 警告处理为错误这里有什么问题
- 什么时候调用组成单元对象的析构函数
- #定义c-预处理器常量..我做错了什么
- 努力将整数转换为链表。不知道我在这里做错了什么
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 计时器坏了或者其他什么的
- 我是否错过了什么,或者虚拟呼叫的性能并不像人们所说的那样糟糕
- vector::empty()出错,或者我遗漏了什么
- 继承Java集合接口(Set、Map、List等)的C++等价物是什么?或者扩展AbstractCollection
- ShellExecute不工作,或者我做错了什么
- 我的字节数组到十六进制字符串转换器有什么问题,或者为什么它在写入文件时会在某个位置后剪切符号?
- 我可以将 std::数组转换为切片吗?或者我还能用什么
- valarray在gcc和msvc(或者Linux和Windows)中有什么不同
- 我的iOS金属计算内核是否存在编译器错误,或者我遗漏了什么
- 如何派生模板函数?或者这个场景的首选方法是什么
- 有什么事情c++可以做得比D更好,或者D做不到的吗?(多重继承的例子)
- 解析是如何工作的,或者是什么使类型完整或不完整
- 构造 函数?或者别的什么
- 为什么 std::noskipws 不起作用,或者它应该做什么?