以下代码片段是来自 SPLASH 2 的 water-nsq 基准测试的代码...
if (comp_last > NMOL1)
{
for (mol = StartMol[ProcID]; mol < NMOL; mol++)
{
pthread_mutex_lock(&gl->MolLock[mol % MAXLCKS]);
for ( dir = XDIR; dir <= ZDIR; dir++) {
temp_p = VAR[mol].F[DEST][dir];
temp_p[H1] += PFORCES[ProcID][mol][dir][H1];
temp_p[O] += PFORCES[ProcID][mol][dir][O];
temp_p[H2] += PFORCES[ProcID][mol][dir][H2];
}
pthread_mutex_unlock(&gl->MolLock[mol % MAXLCKS]);
}
comp = comp_last % NMOL;
for (mol = 0; ((mol <= comp) && (mol < StartMol[ProcID])); mol++)
{
pthread_mutex_lock(&gl->MolLock[mol % MAXLCKS]);
for ( dir = XDIR; dir <= ZDIR; dir++)
{
temp_p = VAR[mol].F[DEST][dir];
temp_p[H1] += PFORCES[ProcID][mol][dir][H1];
temp_p[O] += PFORCES[ProcID][mol][dir][O];
temp_p[H2] += PFORCES[ProcID][mol][dir][H2];
}
pthread_mutex_unlock(&gl->MolLock[mol % MAXLCKS]);
}
}
else
{
for (mol = StartMol[ProcID]; mol <= comp_last; mol++)
{
pthread_mutex_lock(&gl->MolLock[mol % MAXLCKS]);
for ( dir = XDIR; dir <= ZDIR; dir++)
{
temp_p = VAR[mol].F[DEST][dir];
temp_p[H1] += PFORCES[ProcID][mol][dir][H1];
temp_p[O] += PFORCES[ProcID][mol][dir][O];
temp_p[H2] += PFORCES[ProcID][mol][dir][H2];
}
pthread_mutex_unlock(&gl->MolLock[mol % MAXLCKS]);
}
}
pthread_barrier_wait(&(gl->start));
问题在于它最终在屏障处不是确定性的,也就是说,如果您使用相同的输入执行此代码两次,它会给出不同的答案。换句话说,如果改变互斥体的锁定顺序,结果就会不同。
是的,我已经通过记录内存页面验证了这一点。我还可以向您保证,更改发生在 VAR 的(由 temp_p 指向)内存中。
我想知道为什么?因为显然,所有线程都将自己的值 (PFORCES[ProcID]...) 添加到 temp_p 的总和中,最后,即在屏障处,无论线程获取锁的顺序如何,结果都应该相同。
[编辑]
另外,请注意变量 comp、dir 和 mol 都是线程的局部变量,因此不共享。
最佳答案
第二次尝试。
我无法检查它,但我假设在 temp_p[H1] += PFORCES[ProcID][mol][dir][H1];
中您正在添加 double 或 float 。
对于浮点类型,加法的顺序很重要!浮点加法不结合!
不同的线程顺序意味着不同的添加顺序。因此,结果的变化是可以预料的。
参见http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems一些解释。
关于c - 为什么这段代码不是确定性的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6914180/