在这里使random_generator静态有问题吗?

时间:2015-12-17 12:35:12

标签: c++ boost boost-uuid

我需要生成大量的UUID。如果我没有rg静态,那么每次默认构建它都需要很长时间。如果我把它变成静态会有什么问题,它会不会影响uuids的独特性?

有没有更好的方法呢?

using namespace boost::uuids;

uuid generateUUID() {
    static random_generator rg; // here
    return rg();
}

void someFunction() {
    for (int i = 0; i < 1000000; ++i) {
        uuid id = generateUUID();
        // use id
    }
}

2 个答案:

答案 0 :(得分:1)

  

我需要生成大量的UUID。如果我不让rg静态,那么每次默认构建它都需要很多时间。如果我把它变成静态会有什么不对吗,无论如何都会伤害uuids的独特性吗?

不,那很好......

  

有没有更好的方法呢?

...也许,这取决于您将需要的迭代次数。

你看到功能范围内的静态对象的(次要)问题在于标准的措辞,它规定:

a)对象是第一次代码流过语句

时构造的

b)这种结构必须是线程安全的。

实际上,这意味着generateUUID()中的代码必须测试它是否已经构造了对象。这涉及内存提取和条件分支。

这是在apple clang 7.0上使用-O3:

编译时函数的汇编代码输出
__Z12generateUUIDv:                     ## @_Z12generateUUIDv
Lfunc_begin0:
    .cfi_startproc
    .cfi_personality 155, ___gxx_personality_v0
    .cfi_lsda 16, Lexception0
## BB#0:
    pushq   %rbp
Ltmp3:
    .cfi_def_cfa_offset 16
Ltmp4:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp5:
    .cfi_def_cfa_register %rbp
    pushq   %rbx
    subq    $24, %rsp
Ltmp6:
    .cfi_offset %rbx, -24
    movb    __ZGVZ12generateUUIDvE2rg(%rip), %al  ## MEMORY FETCH
    testb   %al, %al                              ## TEST
    jne LBB0_4                                    ## CONDITIONAL BRANCH
## BB#1:
    leaq    __ZGVZ12generateUUIDvE2rg(%rip), %rdi ## another check in case
    callq   ___cxa_guard_acquire                  ## 2+ threads are racing
    testl   %eax, %eax
    je  LBB0_4
...                                               ## object constructed here

这是必要的,因为任何人都可以调用该函数。优化者无法知道施工是否已经发生过。因此,我们不再需要3条额外的指令,因为我们选择了在需要时构建对象的奢侈品。

这意味着如果您处于紧密循环中,可能希望避免所有这些冗余测试。

问题是,创建生成器的成本与300万条指令执行的成本和(希望)正确预测的分支成本是什么?

在大量迭代中,以下代码实际上变得更有效(如果这对您来说真的很重要):

void someFunction() {
    // one generator used for the duration of the function
    random_generator rg;
    for (int i = 0; i < 1000000; ++i) {
        uuid id = rg();
        // use id
    }
}

最有效地使用静态生成器是这样的:

using namespace boost::uuids;

static random_generator rg; // constructed at some point before main() is called
                            // we no longer require flag tests

void someFunction() {
    for (int i = 0; i < 1000000; ++i) {
        uuid id = rg();
        // use id
    }
}

然而,这稍微不方便,因为我们现在必须注意,在调用rg之前不会使用main()(除非您仅在定义它的同一编译单元中使用它)。

答案 1 :(得分:0)

不,这没有问题。