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

时间:2013-07-05 18:23:47

标签: c++ exception try-catch compiler-optimization

我正在为一个类编写单元测试,以便在没有可用内存时测试插入。它依赖于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是一个函数,函数的所有副作用都应该在转到下一行之前完成。投掷是一种副作用。

奖励点:如果正确答案是“是否可以重新排序”,那么两条线路之间的内存屏障是否足以解决它?

1 个答案:

答案 0 :(得分:2)

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

当两个线程读写共享状态时,重新排序变为可见。如果线程A对共享变量进行修改,则线程B可以无序地看到这些修改,或者如果它具有高速缓存的共享状态则根本看不到。这可能是由于线程A或线程B或两者都进行了优化。

但是,线程A将始终按顺序看到自己的修改。每个sequence point必须按顺序发生,至少就本地线程所知。

假设线程A执行了这段代码:

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

每个;引入一个序列点。由于foo()未引入序列点,因此允许编译器首先调用bar()+,无论它们喜欢什么。如果您打印出打印输出,您可能会先看到foo(),或者您可能会先看到bar()。任何一个都是正确的。但是,在将baz分配给b之前,必须先调用它们。如果foo()bar()引发异常,则b 必须保留其现有值。

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

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