Есть ли какие-либо проблемы с тем, чтобы сделать random_generator статическим?

Мне нужно сгенерировать большое количество UUID. Если я не сделаю rg static, это займет много времени при построении по умолчанию каждый раз. Что-то не так, если я сделаю это статичным, не повредит ли это уникальности 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 ответа

Мне нужно сгенерировать большое количество UUID. Если я не сделаю rg статическим, то при его создании по умолчанию потребуется много времени. Что-то не так, если я сделаю это статичным, не повредит ли это уникальности uuids?

Нет, все в порядке...

Есть ли лучший способ сделать это?

... может быть, в зависимости от количества итераций, которые вам понадобятся.

Вы видите (незначительную) проблему со статическими объектами в области видимости функции в формулировке стандарта, которая требует, чтобы:

а) Объект создается при первом прохождении временного кода над оператором и

б) что эта конструкция должна быть поточно-ориентированной.

На практике это означает, что код в 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 дополнительными инструкциями, которые нам на самом деле не нужны, потому что мы выбрали роскошь строительства нашего объекта при необходимости.

А это значит, что если вы находитесь в тесном цикле, вы можете избежать всех этих избыточных тестов.

Вопрос в том, какова стоимость создания генератора по сравнению со стоимостью 3 миллионов выполнений команд и (надеюсь) правильно предсказанной ветви?

При большом количестве итераций следующий код фактически становится более эффективным (если это действительно важно для вас):

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() был вызван (если только вы не используете его в том же модуле компиляции, в котором он был определен).

Нет, с этим проблем нет.

Другие вопросы по тегам