C ++ статическая инициализация - PullRequest
3 голосов
/ 20 сентября 2010

как должно быть поведение в следующем случае:

class C {
    boost::mutex mutex_;
    std::map<...> data_;
};

C& get() {
    static C c;
    return c;
}

int main() {
    get(); // is compiler free to optimize out the call? 
    ....
}

разрешено ли компилятору оптимизировать вызов get()?

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

это лучший вариант?:

C& get() {
    static C *c = new C();
    return *c;
}

Ответы [ 3 ]

4 голосов
/ 20 сентября 2010

Стандарты C и C ++ работают по довольно простому принципу, общеизвестному как правило "как если бы", - в основном, то, что компилятор может делать практически все, что только соответствующий код не может различить то, что он сделал и что официально требовалось.

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

3 голосов
/ 20 сентября 2010

На основе ваших правок приведена улучшенная версия с теми же результатами.

Ввод:

struct C { 
    int myfrob;
    int frob();
    C(int f);
 };
C::C(int f) : myfrob(f) {}
int C::frob() { return myfrob; }

C& get() {
    static C *c = new C(5);
    return *c;
}

int main() {
    return get().frob(); // is compiler free to optimize out the call? 

}

Вывод:

; ModuleID = '/tmp/webcompile/_28088_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"

%struct.C = type { i32 }

@guard variable for get()::c = internal global i64 0            ; <i64*> [#uses=4]

declare i32 @__cxa_guard_acquire(i64*) nounwind

declare i8* @operator new(unsigned long)(i64)

declare void @__cxa_guard_release(i64*) nounwind

declare i8* @llvm.eh.exception() nounwind readonly

declare i32 @llvm.eh.selector(i8*, i8*, ...) nounwind

declare void @__cxa_guard_abort(i64*) nounwind

declare i32 @__gxx_personality_v0(...)

declare void @_Unwind_Resume_or_Rethrow(i8*)

define i32 @main() {
entry:
  %0 = load i8* bitcast (i64* @guard variable for get()::c to i8*), align 8 ; <i8> [#uses=1]
  %1 = icmp eq i8 %0, 0                           ; <i1> [#uses=1]
  br i1 %1, label %bb.i, label %_Z3getv.exit

bb.i:                                             ; preds = %entry
  %2 = tail call i32 @__cxa_guard_acquire(i64* @guard variable for get()::c) nounwind ; <i32> [#uses=1]
  %3 = icmp eq i32 %2, 0                          ; <i1> [#uses=1]
  br i1 %3, label %_Z3getv.exit, label %bb1.i

bb1.i:                                            ; preds = %bb.i
  %4 = invoke i8* @operator new(unsigned long)(i64 4)
          to label %invcont.i unwind label %lpad.i ; <i8*> [#uses=2]

invcont.i:                                        ; preds = %bb1.i
  %5 = bitcast i8* %4 to %struct.C*               ; <%struct.C*> [#uses=1]
  %6 = bitcast i8* %4 to i32*                     ; <i32*> [#uses=1]
  store i32 5, i32* %6, align 4
  tail call void @__cxa_guard_release(i64* @guard variable for get()::c) nounwind
  br label %_Z3getv.exit

lpad.i:                                           ; preds = %bb1.i
  %eh_ptr.i = tail call i8* @llvm.eh.exception()  ; <i8*> [#uses=2]
  %eh_select12.i = tail call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* %eh_ptr.i, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* null) ; <i32> [#uses=0]
  tail call void @__cxa_guard_abort(i64* @guard variable for get()::c) nounwind
  tail call void @_Unwind_Resume_or_Rethrow(i8* %eh_ptr.i)
  unreachable

_Z3getv.exit:                                     ; preds = %invcont.i, %bb.i, %entry
  %_ZZ3getvE1c.0 = phi %struct.C* [ null, %bb.i ], [ %5, %invcont.i ], [ null, %entry ] ; <%struct.C*> [#uses=1]
  %7 = getelementptr inbounds %struct.C* %_ZZ3getvE1c.0, i64 0, i32 0 ; <i32*> [#uses=1]
  %8 = load i32* %7, align 4                      ; <i32> [#uses=1]
  ret i32 %8
}

Примечательно, код отсутствуетиспускается для :: get, но main по-прежнему выделяет :: get :: c (на% 4) с защитной переменной по мере необходимости (на% 2 и в конце invcont.i и lpad.i).Здесь вы найдете все это.

tl; dr: Не беспокойтесь об этом, оптимизатор обычно делает все правильно.Вы видите ошибку?

1 голос
/ 20 сентября 2010

Оптимизирует ли компилятор вызов функции или нет, это в основном не определенное поведение в соответствии со Стандартом.Неопределенное поведение - это, в основном, поведение, которое выбирается из набора конечных возможностей, но выбор может быть непостоянным каждый раз.В этом случае выбор заключается в том, чтобы «оптимизировать» или «нет», что в стандарте не указано, и реализация также не должна документироваться, поскольку это выбор, который может не всегда приниматься данной реализацией.

Если идея состоит в том, чтобы просто «дотронуться», поможет ли это, если мы просто добавим фиктивную переменную и подставим ее в каждый вызов

например

C& getC(){
   volatile int dummy;
   dummy++;
   // rest of the code
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...