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