Is it possible to remove dispatch_once in Objective-C ++?

Since C ++ 11, local variables staticare known to be initialized in a thread-safe manner (if not specified -fno-threadsafe-statics), as indicated in this question . Does this mean that the following well-known scheme:

+ (NSObject *)onlyOnce {
  static NSObject *object;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    object = [[NSObject alloc] init];
  });
  return object;
}

Can be replaced much shorter:

+ (NSObject *)onlyOnce {
  static NSObject *object = [[NSObject alloc] init];
  return object;
}

When compiling code as Objective-C ++ with a C ++ language dialect of C ++ 11 and above?

+4
source share
2 answers

TL DR - it seems that you can use C ++ 11 static initialization in streaming safe mode, which has the same performance characteristics as dispatch_once.

, ++:

class Object {  
};

static Object *GetObjectCppStatic() {
  static Object *object = new Object();
  return object;
}

int main() {
  GetObjectCppStatic();
}

clang++ test.cpp -O0 -fno-exceptions -S (-O0, inlining, -Os, -fno-exceptions ), , GetObjectCppStatic :

__ZL18GetObjectCppStaticv:        ## @_ZL18GetObjectCppStaticv
  .cfi_startproc
## BB#0:
  pushq   %rbp
Lcfi6:
  .cfi_def_cfa_offset 16
Lcfi7:
  .cfi_offset %rbp, -16
  movq  %rsp, %rbp
Lcfi8:
  .cfi_def_cfa_register %rbp
  cmpb  $0, __ZGVZL18GetObjectCppStaticvE6object(%rip)
  jne LBB2_3
## BB#1:
  leaq  __ZGVZL18GetObjectCppStaticvE6object(%rip), %rdi
  callq   ___cxa_guard_acquire
  cmpl  $0, %eax
  je  LBB2_3
## BB#2:
  movl  $1, %eax
  movl  %eax, %edi
  callq   __Znwm
  leaq  __ZGVZL18GetObjectCppStaticvE6object(%rip), %rdi
  movq  %rax, __ZZL18GetObjectCppStaticvE6object(%rip)
  callq   ___cxa_guard_release
LBB2_3:
  movq  __ZZL18GetObjectCppStaticvE6object(%rip), %rax
  popq  %rbp
  retq
  .cfi_endproc

___cxa_guard_acquire ___cxa_guard_release, lib++ ABI . , clang, ++ 11, , -, .

, , . ? () ():

#include <cstdio>
#include <dispatch/dispatch.h>
#include <mach/mach_time.h>

class Object {  
};

static double Measure(int times, void(^executionBlock)(), void(^finallyBlock)()) {
  struct mach_timebase_info timebaseInfo;
  mach_timebase_info(&timebaseInfo);

  uint64_t start = mach_absolute_time();
  for (int i = 0; i < times; ++i) {
    executionBlock();
  }
  finallyBlock();
  uint64_t end = mach_absolute_time();

  uint64_t timeTook = end - start;
  return ((double)timeTook * timebaseInfo.numer / timebaseInfo.denom) /
      NSEC_PER_SEC;
}

static Object *GetObjectDispatchOnce() {
  static Object *object;
  static dispatch_once_t onceToken;

  dispatch_once(&onceToken, ^{
    object = new Object();
  });

  return object;
}

static Object *GetObjectCppStatic() {
  static Object *object = new Object();
  return object;
}

int main() {
  printf("Single thread statistics:\n");
  printf("DispatchOnce took %g\n", Measure(10000000, ^{
    GetObjectDispatchOnce();
  }, ^{}));
  printf("CppStatic took %g\n", Measure(10000000, ^{
    GetObjectCppStatic();
  }, ^{}));

  printf("\n");

  dispatch_queue_t queue = dispatch_queue_create("queue", 
      DISPATCH_QUEUE_CONCURRENT);
  dispatch_group_t group = dispatch_group_create();

  printf("Multi thread statistics:\n");
  printf("DispatchOnce took %g\n", Measure(1000000, ^{
    dispatch_group_async(group, queue, ^{
      GetObjectDispatchOnce();
    });
  }, ^{
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
  }));
  printf("CppStatic took %g\n", Measure(1000000, ^{
    dispatch_group_async(group, queue, ^{
      GetObjectCppStatic();
    });
  }, ^{
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
  }));
}

x64:

Single thread statistics:
DispatchOnce took 0.025486
CppStatic took 0.0232348

Multi thread statistics:
DispatchOnce took 0.285058
CppStatic took 0.32596

, , , , - , . dispatch_once _dispatch_once:

void
_dispatch_once(dispatch_once_t *predicate,
    DISPATCH_NOESCAPE dispatch_block_t block)
{
  if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
    // ...
  } else {
    // ...
  }
}

++ ___cxa_guard_acquire.

0

, Objective-C ++, Objective-C - ++, , "" ++ 11. , ++ 11 Objectiver-++. , , , Objective-C ++.

, , " ", , " " dispatch_once "" "notOnlyOnce" , ( , ).

, " " , " " , "notOnlyOnce" . , , " " , , . " " " " , "notOnlyOnce" , , , .

. .

@interface SingletonClass : NSObject

- (instancetype)init;

@end

@implementation SingletonClass

- (instancetype)init {
    self = [super init];
    std::cout << "Created a singleton object" << std::endl;
    for (int i=0; i<1000000; i++) { i++; }
    return self;
}

@end

@interface TestClassObjCPP : NSObject 

@property (nonatomic) SingletonClass *sc;

+ (SingletonClass *)onlyOnceNewStyle;
+ (SingletonClass *)onlyOnceOldStyle: (TestClassObjCPP*)caller;
+ (SingletonClass *)notOnlyOnce: (TestClassObjCPP*)caller;

@end

@implementation TestClassObjCPP


+ (SingletonClass *)onlyOnceNewStyle {
    static SingletonClass *object = [[SingletonClass alloc] init];
    return object;
}

+ (SingletonClass *)onlyOnceOldStyle: (TestClassObjCPP*)caller {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        caller.sc = [[SingletonClass alloc] init];
    });

    return caller.sc;
}

+ (SingletonClass *)notOnlyOnce: (TestClassObjCPP*)caller {

    if (caller.sc == nil)
        caller.sc = [[SingletonClass alloc] init];

    return caller.sc;
}

@end


int main(int argc, char * argv[]) {


    @autoreleasepool {

        std::cout << "Before loop requesting singleton." << std::endl;
        TestClassObjCPP *caller = [[TestClassObjCPP alloc] init];
        caller.sc = nil;
        for (int i=0; i<10000; i++) {
            dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [TestClassObjCPP onlyOnceNewStyle];  // (1)
                // [TestClassObjCPP onlyOnceOldStyle:caller]; // (2)
                // [TestClassObjCPP notOnlyOnce:caller]; // (3)
            });

        }
        std::cout << "After loop requesting singleton." << std::endl;

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

onlyOnceNewStyle (1):

Before loop requesting singleton.
Created a singleton object
After loop requesting singleton.

onlyOnceOldStyle (2):

Before loop requesting singleton.
Created a singleton object
After loop requesting singleton.

notOnlyOnce (3):

Before loop requesting singleton.
Created a singleton object
Created a singleton object
Created a singleton object
After loop requesting singleton.

, , .

+1

Source: https://habr.com/ru/post/1690052/


All Articles