Подтвердить что ты не робот

Какой лучший способ запустить дорогостоящую инициализацию?

У меня есть синглтон, который стоит дорого инициализировать:

struct X {...};

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

В первый раз, когда вызывается get_X(), для инициализации функции-local static может потребоваться сотни миллисекунд. Но после этого все, что мне нужно сделать с X, относительно быстро:

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

Как я могу свести к минимуму большую задержку при первом вызове get_X()? У меня много ядер...

4b9b3361

Ответ 1

Как только ваше приложение запустится, и (надеюсь), прежде чем вам действительно нужно позвонить get_X(), безвозмездно вызовите его. Кроме того, чтобы ваша стадия инициализации была быстрее, подайте свои дорогостоящие инициализации на разные потоки. Например:

#include <thread>

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

Когда какая-то задача стоит так дорого, как несколько сотен миллисекунд (или больше), накладные расходы на отключение потока для борьбы с ним - это уровень шума. И если вы работаете на многоядерном оборудовании (что не в наши дни?), То это четкий выигрыш в производительности, если ваше приложение действительно ничего не нуждается в этом синглтоне, пока не завершится первоначальный вызов get_X.

Примечания/Вопросы:

  • Почему detach thread? Почему бы не join?

    Если вы решите join этот thread, это означает, что вам просто нужно подождать, пока он закончит, а почему бы и нет. Когда он закончит, detach очистится после себя. Вам даже не нужно сохранять дескриптор thread. Временный std::thread разрушает, но поток ОС продолжает работать, завершая get_X.

    Когда стандартизован thread, были точки зрения, что detach ed thread были не только бесполезны, но и опасны. Но вот совершенно безопасный и вполне мотивирующий прецедент для detach ed thread s.

  • Что делать, если мое приложение вызывает get_X() до того, как detach ed thread завершит первый вызов get_X()?

    Появляется удар производительности, но не попадание правильности. Ваше приложение будет блокироваться в этой строке в get_X():

    static const X x = init_X();

    пока detach ed thread не закончит выполнение. Таким образом, нет расы данных.

  • Что делать, если мое приложение заканчивается до завершения detach ed thread?

    Если ваше приложение заканчивается на этапе инициализации, что-то, очевидно, катастрофически ошибочно. Если get_X касается чего-то, что уже разрушено цепочкой at_exit (которая выполняется после main), произойдут плохие вещи. Тем не менее, вы уже находитесь в состоянии панического отключения... еще одна аварийная ситуация вряд ли ухудшит вашу панику. Ты уже мертв. Otoh, если ваша инициализация - это что-то, что занимает от нескольких минут до нескольких часов, вам, вероятно, нужно улучшить общение о завершении инициализации и более грациозных процедурах закрытия. В этом случае вам необходимо осуществить совместное аннулирование в ваших потоках, отключенных или нет (то, что комитет std отказался предоставить вам).

  • Что делать, если первый вызов get_X() вызывает исключение?

    В этом случае второй вызов get_X() имеет возможность инициализировать функцию local static, предполагая, что вы не оставите исключение неотображаемым и разрешите ему завершить работу вашей программы. Он может слишком бросаться, или может преуспеть в инициализации (это зависит от вашего кода). В любом случае вызовы get_X() будут продолжать пытаться инициализировать, ожидая выполнения инициализации, пока кому-то не удастся сделать это, не выбрасывая исключение. И это все правда, поступают ли вызовы из разных потоков или нет.

В заключение

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

- это хороший способ использовать мощь ваших нескольких ядер, чтобы как можно быстрее получить независимые дорогостоящие инициализации, без ущерба для правильной точности потока.

Единственным недостатком является то, что вы инициализируете данные в get_X(), нужен ли вам это или нет. Поэтому убедитесь, что вам это понадобится, прежде чем использовать эту технику.


[Сноска] Для тех, кто использует Visual Studio, это хорошая мотивация для перехода на VS-2015. До этой версии VS не реализует потокобезопасную функцию - локальную статику.

Ответ 2

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

#include <iostream>

class X {
public:
    static X const& get_x();
private:
    static X const x;

    X()
    {
        std::cout << "X init" << std::endl;
    }
};

int main()
{
    std::cout << "Enter main" << std::endl;
    X::get_x();
    return 0;
}

X const X::x;

X const& X::get_x()
{
    return X::x;
}