【问题标题】:Can code reordering affect my test代码重新排序会影响我的测试吗
【发布时间】:2013-07-05 18:23:47
【问题描述】:

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

奖励点:如果正确答案是“是的,可能会发生重新排序”,那么两行之间的内存屏障是否足以修复它?

【问题讨论】:

  • C/C++ 规范要求优化都具有“好像”优化没有发生的结果。但是,这仅在执行时才实用,并且仅在程序的常规执行中需要。诸如 POSIX SIGHUP 和线程之类的操作系统信号超出了 C++ 规范,并打破了“as-if”假设。在您的情况下,即使您将其称为 signalReceived,它也不是 POSIX 信号,也不会成为问题。

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


【解决方案1】:

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

当两个线程读取和写入共享状态时,重新排序变得可见。如果线程 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 会知道。多线程编程中的问题是序列点不适用于其他线程。这就是内存屏障的用武之地。内存屏障在某种意义上是跨线程序列点。

【讨论】:

  • 我一头雾水:编译器怎么提前知道代码是否是多线程的?
  • @qdii 一个不同的线程可以观察到关于该线程如何修改共享状态的无序行为,但该线程将始终看到它自己的操作按顺序发生。
猜你喜欢
  • 2015-02-22
  • 1970-01-01
  • 2013-02-17
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
  • 2020-01-13
相关资源
最近更新 更多