gpt4 book ai didi

c# - GLSL 自旋锁永远阻塞

转载 作者:行者123 更新时间:2023-11-30 18:16:24 27 4
gpt4 key购买 nike

我正在尝试在 GLSL 中实现自旋锁。它将在 Voxel Cone Tracing 的上下文中使用。我尝试将存储锁定状态的信息移动到允许原子操作的单独 3D 纹理。为了不浪费内存,我不使用完整的整数来存储锁定状态,而是只使用一个位。问题是在不限制最大迭代次数的情况下,循环永远不会终止。我在 C# 中实现了完全相同的机制,创建了很多在共享资源上工作的任务,并且在那里工作得很好。Euro Par 2017:并行处理第 274 页(可在 Google 上找到)一书提到了在 SIMT 设备上使用锁时可能需要注意的事项。我认为代码应该绕过这些警告。

有问题的 GLSL 代码:

void imageAtomicRGBA8Avg(layout(RGBA8) volatile image3D image, layout(r32ui) volatile uimage3D lockImage,
ivec3 coords, vec4 value)
{
ivec3 lockCoords = coords;

uint bit = 1<<(lockCoords.z & (4)); //1<<(coord.z % 32)
lockCoords.z = lockCoords.z >> 5; //Division by 32

uint oldValue = 0;
//int counter=0;
bool goOn = true;
while (goOn /*&& counter < 10000*/)
//while(true)
{
uint newValue = oldValue | bit;
uint result = imageAtomicCompSwap(lockImage, lockCoords, oldValue, newValue);

//Writing is allowed if could write our value and if the bit indicating the lock is not already set
if (result == oldValue && (result & bit) == 0)
{
vec4 rval = imageLoad(image, coords);
rval.rgb = (rval.rgb * rval.a); // Denormalize
vec4 curValF = rval + value; // Add
curValF.rgb /= curValF.a; // Renormalize
imageStore(image, coords, curValF);

//Release the lock and set the flag such that the loops terminate
bit = ~bit;
oldValue = 0;
while (goOn)
{
newValue = oldValue & bit;
result = imageAtomicCompSwap(lockImage, lockCoords, oldValue, newValue);
if (result == oldValue)
goOn = false; //break;
oldValue = result;
}
//break;
}
oldValue = result;
//++counter;
}
}

具有相同功能的工作 C# 代码

public static void Test()
{
int buffer = 0;
int[] resource = new int[2];
Action testA = delegate ()
{
for (int i = 0; i < 100000; ++i)
imageAtomicRGBA8Avg(ref buffer, 1, resource);
};
Action testB = delegate ()
{
for (int i = 0; i < 100000; ++i)
imageAtomicRGBA8Avg(ref buffer, 2, resource);
};

Task[] tA = new Task[100];
Task[] tB = new Task[100];
for (int i = 0; i < tA.Length; ++i)
{
tA[i] = new Task(testA);
tA[i].Start();
tB[i] = new Task(testB);
tB[i].Start();
}

for (int i = 0; i < tA.Length; ++i)
tA[i].Wait();
for (int i = 0; i < tB.Length; ++i)
tB[i].Wait();
}

public static void imageAtomicRGBA8Avg(ref int lockImage, int bit, int[] resource)
{
int oldValue = 0;
int counter = 0;
bool goOn = true;
while (goOn /*&& counter < 10000*/)
{
int newValue = oldValue | bit;
int result = Interlocked.CompareExchange(ref lockImage, newValue, oldValue); //imageAtomicCompSwap(lockImage, lockCoords, oldValue, newValue);
if (result == oldValue && (result & bit) == 0)
{
//Now we hold the lock and can write safely
resource[bit - 1]++;

bit = ~bit;
oldValue = 0;
while (goOn)
{
newValue = oldValue & bit;
result = Interlocked.CompareExchange(ref lockImage, newValue, oldValue); //imageAtomicCompSwap(lockImage, lockCoords, oldValue, newValue);
if (result == oldValue)
goOn = false; //break;
oldValue = result;
}
//break;
}
oldValue = result;
++counter;
}
}

锁定机制的工作方式应该与 Cyril Crassin 和 Simon Green 在 OpenGL Insigts 第 22 章使用 GPU 硬件光栅器进行基于八叉树的稀疏体素化中描述的机制完全相同。他们只使用整数纹理来存储我想避免的每个体素的颜色,因为这会使 Mip 映射和其他事情复杂化。我希望帖子是可以理解的,我觉得它已经变得太长了......

为什么 GLSL 实现没有终止?

最佳答案

如果我理解得很好,你可以使用 lockImage作为线程锁:确定坐标处的确定值意味着“只有此着色器实例可以执行下一步操作”(在该坐标处更改其他图像中的数据)。正确的。

关键是imageAtomicCompSwap .我们知道它完成了这项工作,因为它能够存储确定的值(假设 0 表示“免费”,1 表示“锁定”)。我们知道它是因为返回值(原始值)是“免费的”(即交换操作发生了):

bool goOn = true;
unit oldValue = 0; //free
uint newValue = 1; //locked
//Wait for other shader instance to free the simulated lock
while ( goON )
{
uint result = imageAtomicCompSwap(lockImage, lockCoords, oldValue, newValue);
if ( result == oldValue ) //it was free, now it's locked
{
//Just this shader instance executes next lines now.
//Other instances will find a "locked" value in 'lockImage' and will wait
...
//release our simulated lock
imageAtomicCompSwap(lockImage, lockCoords, newValue, oldValue);
goOn = false;
}
}

我认为你的代码永远循环,因为你用 bit 复杂化了你的生活var 并错误地使用了 oldValenewValue

编辑:

如果 lockImage 的 'z'是 32 的倍数(只是一个理解的提示,不需要精确的倍数),您尝试将 32 个体素锁打包成一个整数。我们称这个整数为32C .

着色器实例(“SI”)可能想要更改其在 32C 中的位,锁定或解锁。因此,您必须 (A) 获取当前值并且 (B) 仅更改您的位。

其他 SI 正在尝试更改其位。有些位相同,有些位不同。

两次调用 imageAtomicCompSwap 之间在一个 SI 中,其他 SI 可能改变的不是你的位(它被锁定了,不是吗?),而是同一 32C 中的其他位。值(value)。你不知道哪个是当前值,你只知道你的位。因此,您在 imageAtomicCompSwap 中没有任何东西(或旧的错误值)可以比较称呼。它可能无法设置新值。几个 SI 失败会导致“死锁”并且 while 循环永远不会结束。

您尝试通过 oldValue = result 避免使用旧的错误值并再次尝试 imageAtomicCompSwap .这是我之前写的(A)-(B)。但是在 (A) 和 (B) 之间还有其他 SI 可能已经改变了 result= 32C值(value),毁了你的想法。

想法:您可以使用我的简单方法(仅 01 中的值 lockImage ),无需 bits事物。结果是 lockImage更小。但是所有着色器实例都试图更新 32 个 image 中的任何32C 相关的坐标lockImage 中的值将等到锁定该值的人释放它。

使用另一个lockImage2只是为了锁定-解锁 32C有点更新的值(value),似乎旋转太多。

关于c# - GLSL 自旋锁永远阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46632261/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com