代码重新排序会影响我的测试吗

Can code reordering affect my test

本文关键字:影响 我的 测试 排序 新排序 代码      更新时间:2023-10-16

我正在为一个类编写一个单元测试,以便在没有可用内存的情况下测试插入。它依赖于nbElementInsertedinsert_edge返回后递增的事实。

void test()
{
    adjacency_list a(true);
    MemoryVacuum no_memory_after_this_line;
    bool signalReceived = false;
    size_t nbElementInserted = 0;
    do
    {
        try
        {
            a.insert_edge( 0, 1, true ); // this should throw
            nbElementInserted++; 
        }
        catch(std::bad_alloc &)
        {
            signalReceived = true;
        }
    }
    while (!signalReceived); // this loop is necessary because the 
                             // memory vacuum only prevents new memory
                             // pages from being mapped. so the first
                             // allocations may succeed.
    CHECK_EQUAL( nbElementInserted, a.nb_edges() );
}

现在我想知道这两种说法中哪一种是真的:

  • 可以进行重新排序,在这种情况下,nbElementInserted可以在insert_edge引发异常之前递增,这将使我的情况无效。重新排序可能发生,因为如果两行被排列,用户的可见结果是相同的
  • 重新排序不能发生,因为insert_edge是一个函数,在转到下一行之前应该完成该函数的所有副作用。投掷是一种副作用

加分:如果正确答案是"是的,可以重新排序",那么两行之间的内存屏障是否足以修复它?

否。重新排序只在多线程或多处理场景中发挥作用。在单个线程中,编译器不能以改变程序行为的方式对指令进行重新排序。例外情况不属于此规则的例外情况。

当两个线程读写到共享状态时,可以看到重新排序。如果线程A对共享变量进行了修改,那么线程B可能会看到这些无序的修改,甚至在缓存了共享状态的情况下根本看不到。这可能是由于线程A或线程B或两者中的优化。

线程A总是按顺序看到自己的修改。每个序列点必须按顺序发生,至少就本地线程所知是这样。

假设线程A执行了以下代码:

a = foo() + bar();
b = baz;

每个;引入一个序列点。编译器可以先调用foo()bar(),不管它喜欢什么,因为+不引入序列点。如果打印输出,您可能会看到先调用foo(),也可能会看到首先调用bar()。任何一个都是正确的。不过,在将baz分配给b之前,它必须调用它们。如果foo()bar()抛出异常,则b必须保留其现有值。

然而,如果编译器知道foo()bar()从不抛出,并且它们的执行在任何方面都不取决于b的值,那么它可以重新排序这两个语句。这将是一个有效的优化。线程A将无法知道语句已被重新排序。

另一方面,线程B会知道。多线程编程的问题是序列点不适用于其他线程。这就是内存屏障的作用。从某种意义上说,内存屏障是跨线程序列的点。