金属计算值与CPU值不同



我正在尝试实现3DPoints向量的长度,当我将GPU检索到的值与CPU进行比较时,它们并不完全相同,通常有大量差异。我最初使用的是packed_float3,它带来了更多的差异,所以我开始使用float3并进行了一些改进,但仍有一些差异我想解决。

这些值相差不大,平均相差-0.00000000048358334004,但当我运行像求和和和减去两个数组这样的操作时,不会出现差异,我希望它也会发生。

这是代码的一部分

主.m

- (void) lenght_function:(NSArray*) array {
_buffer[0] = [_mDevice newBufferWithLength:_sp_size_alloc options:MTLResourceStorageModeShared];
_buffer[1] = [_mDevice newBufferWithLength:_sp_size_alloc options:MTLResourceStorageModeShared];
float3 *datapt = [_buffer[0] contents];
for (unsigned long index = 0 ; index< _sp_lenght ; index++) {
datapt[index].x = (float)[array[index] getX];
datapt[index].y = (float)[array[index] getY];
datapt[index].z = (float)[array[index] getZ];

}
commandBuffer = [_mCommandQueue commandBuffer];
assert(commandBuffer != nil);

id<MTLComputeCommandEncoder> computeEncoder = [commandBuffer computeCommandEncoder];
assert(computeEncoder != nil);
[computeEncoder setComputePipelineState:_mLenghtFunctionPSO];

[computeEncoder setBuffer:_buffer[0] offset:0 atIndex:0];
[computeEncoder setBuffer:_buffer[1] offset:0 atIndex:1];
//[array1 makeData];

MTLSize gridSize = MTLSizeMake(_sp_lenght, 1, 1);

NSUInteger threadGroupSize = _mLenghtFunctionPSO.maxTotalThreadsPerThreadgroup;
if(threadGroupSize > _sp_lenght){
threadGroupSize = _sp_lenght;
}

MTLSize threadgroupsize = MTLSizeMake(threadGroupSize, 1, 1);

[computeEncoder dispatchThreads:gridSize threadsPerThreadgroup:threadgroupsize];
[computeEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
float3 *arr1 = _buffer[0].contents;
float* result = _buffer[1].contents;
unsigned long counter = 0;
for (unsigned long index = 0; index < _sp_lenght; index++)
{
if (result[index] != sqrtf(arr1[index].x*arr1[index].x + arr1[index].y*arr1[index].y + arr1[index].z*arr1[index].z)){
counter++;;
}
}
NSLog(@"ERROR counter %lun",counter);
}

kernel.metal

kernel void lenght(const device float3 *arr1,
device float *result,
uint index[[thread_position_in_grid]]){

result[index] = precise::sqrt(precise::pow(arr1[index].x,2) + precise::pow(arr1[index].y,2) + precise::pow(arr1[index].z,2));
}

32位精度仅为小数点后7位,显示的差值约为小数点前9-10位。因此,您所展示的实际上比预期的32位浮点精度要好一点。听起来您想要64位双精度,但这不是内置的Metal数据类型。

如果你把这些值乘以100或1000,把小数点向上移动,然后在你的值相加后除以这个数字,这可能会有所帮助。

另一种可能性是首先规范化你的值,这样它们都在0到1的范围内。然后你甚至可以使用半精度。

最新更新