什么是运行昂贵的初始化的最好的方法? [英] What's the best way to run an expensive initialization?

查看:81
本文介绍了什么是运行昂贵的初始化的最好的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个单身,初始化很昂贵:

  struct X {...} 

const X&
get_X()
{
static const X x = init_X();
return x;
}

第一次 get_X $ c>被调用,它可能需要几百毫秒来初始化函数局部静态。但之后,我需要做的事情与 X 相对较快:

  get_X()。find_something_for_me(); //如果这是第一次调用
get_X(),那么代价是很贵的; find_something_for_me(); //现在快

如何最大限度地减少第一次调用 get_X()?我有很多核心...

解决方案

一旦你的应用程序启动, get_X(),免费调用它。此外,使您的初始化阶段更快,将昂贵的初始化提供给不同的线程。例如:

  #include< thread> 

int
main()
{
std :: thread(get_X).detach();
//继续其他初始化...
}

是与几百毫秒(或更多)一样昂贵,离开一个线程来处理它的开销是在噪声水平。如果你是多核硬件(这是什么不是这些天?),那么这是一个明确的性能赢如果你的应用程序实际上不需要任何东西从这个单例,直到最初调用 get_X



注意/问题:




  • 为什么分离 线程?为什么加入?



    如果你决定加入这个线程,这意味着你只需要等待它完成,为什么不做其他的事情。当它完成后, detach 它自己清理。你甚至不需要保留线程的句柄。临时 std :: thread 会销毁,但是操作系统线程仍然运行,运行 get_X 完成。



    线程被标准化时,有一些观点 detach ed thread 不仅没有用,而且危险。但是这里是一个完全安全的,并且相当激励的用例分离 ed 线程

    li>
  • 如果我的应用程序在 detach 之前调用 get_X() 线程完成第一次调用 get_X()



    有一个性能命中,但不是正确性命中。您的应用程序将在 get_X()中阻止此行:



    static const X x = <= c $ c> = init_X();



    <线程
    完成执行它。


  • 如果我的应用程序在分离之前结束 ed thread 是否完成?



    如果应用程序在初始化阶段结束,那么显然出现了一些灾难性的错误。如果 get_X 碰到已被 at_exit 链(在main之后执行)销毁的东西,就会发生错误。但是,你已经处于紧急关机的状态...一个更紧急情况不可能使你的恐慌关闭更糟。你已经死了。 Otoh,如果你的初始化是需要几分钟到几小时,你可能需要更好地沟通,当你的初始化完成,更多的关闭程序。在这种情况下,你需要在你的线程中实现协作取消,分离或不分离(std委员会拒绝为你提供的东西)。


  • 调用 get_X()抛出异常?



    在这种情况下,第二次调用 get_X()有机会初始化函数local static,假设你没有留下异常未捕获并允许它终止你的程序。它可能太too了,或者它可能成功初始化(这是由你的代码)。在任何情况下,对 get_X()的调用将继续尝试初始化,等待初始化正在进行,直到有人设法这样做而不抛出异常。




总结

p>

  std :: thread(get_X).detach(); 

是利用多个内核的强大功能获得独立的昂贵初始化



唯一的缺点是你在 get_X()中初始化数据是否需要它。因此,在使用此技术之前,请确保您需要它。






[Footnote]动机移动到VS-2015。在此版本之前VS不实现线程安全的函数局部静态。


I've got a singleton that is expensive to initialize:

struct X {...};

const X&
get_X()
{
    static const X x = init_X();
    return x;
}

The first time get_X() is called, it can take hundreds of milliseconds to initialize the function-local static. But after that is done, the things I need to do with the X are relatively fast:

get_X().find_something_for_me();  // expensive if this is the first call
get_X().find_something_for_me();  // now fast

How can I minimize having a large delay the first time I call get_X()? I have plenty of cores...

解决方案

As soon as your application starts, and (hopefully) before you actually need to call get_X(), gratuitously call it. Furthermore, so that your initialization stage is quicker, feed your expensive initializations off to different threads. For example:

#include <thread>

int
main()
{
    std::thread(get_X).detach();
    // continue with other initialization...
}

When some task is as expensive as several hundred milliseconds (or more), the overhead of spinning off a thread to deal with it is in the noise level. And if you are on multi-core hardware (what isn't these days?), then this is a clear performance win if your application doesn't actually need anything from this singleton until the initial call to get_X completes.

Notes/Questions:

  • Why detach the thread? Why not join?

    If you decide to join this thread, that means you just have to wait for it to finish, why not do other things instead. When it finishes, detach has it clean up after itself. You don't even need to retain a handle to the thread. The temporary std::thread destructs, but the OS thread lives on, running get_X to completion.

    When thread was being standardized, there were viewpoints that detached threads were not only useless, but dangerous. But here is a perfectly safe, and quite motivating use case for detached threads.

  • What if my application calls get_X() before the detached thread finishes the first call to get_X()?

    There is a performance hit, but not a correctness hit. Your application will block at this line in get_X():

    static const X x = init_X();

    until the detached thread is finished executing it. Thus there is no data-race.

  • What if my application ends before the detached thread is complete?

    If your application ends during the initialization stage, something has evidently gone catastrophically wrong. If get_X touches something that is already destructed by the at_exit chain (which executes after main), bad things will happen. However, you are already in a state of panic shutdown ... one more emergency isn't likely to make your panic shutdown worse. You're already dead. Otoh, if your initialization is something that takes minutes to hours, you probably do need better communication about when your initialization is complete, and more graceful shutdown procedures. In that case you need to implement cooperative cancelation in your threads, detached or not (something the std committee declined to provide you with).

  • What if the first call to get_X() throws an exception?

    In that case, the second call to get_X() has its chance to initialize the function local static, assuming you don't leave the exception uncaught and allow it to terminate your program. It may too throw, or it may succeed in initialization (that is up to your code). In any case, calls to get_X() will continue to try to initialize, waiting if initialization is in progress, until somebody manages to do so without throwing an exception. And this is all true whether or not the calls are coming in from different threads or not.

In summary

std::thread(get_X).detach();

is a good way to harness the power of your multiple cores to get independent expensive initializations out of the way as quickly as possible, without compromising thread-safety correctness.

The only downside is that you initialize the data within get_X() whether you need it or not. So be sure you will need it before using this technique.


[Footnote] For those using Visual Studio, this is good motivation to move to VS-2015. Prior to this version VS does not implement thread-safe function-local statics.

这篇关于什么是运行昂贵的初始化的最好的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆