C ++: высокоскоростной стек - PullRequest
1 голос
/ 02 марта 2010

Насколько я полагаю, std :: stack и все такие стеки ручной работы работают намного медленнее, чем стек, который является приложениями.

Может, уже есть хороший "велосипед" низкого уровня? (Реализация стека).

Или это хорошая идея - создать новый поток и использовать его собственный стек?

А как я могу работать напрямую со стеком приложений? (только asm {}?)

Ответы [ 5 ]

4 голосов
/ 02 марта 2010

std :: stack - это коллекция объектов c ++, которые имеют семантику стека. Это не имеет никакого отношения к стеку потока или к внедрению push и pop в ассемблерный код.

Что вы пытаетесь сделать

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

Если вам нужна коллекция со семантикой стека и вы пишете на c ++, тогда std :: stack - ваш выбор, если вы не можете доказать , что это недостаточно быстро

3 голосов
/ 02 марта 2010

Единственный способ, с помощью которого std::stack значительно медленнее, чем стек процессора, - это выделение памяти из свободного хранилища. По умолчанию он использует std::deque для хранения, который распределяет память по частям по мере необходимости. До тех пор, пока вы не продолжите уничтожать и воссоздавать стек, он будет сохранять эту память и не должен выделять больше, если он не станет больше, чем раньше. Итак, структура кода выглядит так:

std::stack<int> stack;
for (int i = 0; i < HUGE_NUMBER; ++i) 
    do_lots_of_work(stack); // uses stack

вместо:

for (int i = 0; i < HUGE_NUMBER; ++i)
    do_lots_of_work(); // creates its own stack

Если после профилирования вы обнаружите, что выделение памяти все еще занимает слишком много времени, вы можете предварительно выделить большой блок, поэтому при запуске программы вам потребуется только одно выделение (при условии, что вы можете найти верхний предел размера стека ). Для этого вам нужно попасть во внутренности стека, но это возможно, если вы получите собственный тип стека. Как то так (не проверено):

class PreallocatedStack : public std::stack< int, std::vector<int> >
{
public:
    explicit PreallocatedStack(size_t size) { c.reserve(size); }
};

РЕДАКТИРОВАТЬ: это довольно ужасный хак, но он поддерживается стандартом C ++. Более вкусным было бы инициализировать стек зарезервированным вектором за счет дополнительного выделения. И не пытайтесь использовать этот класс полиморфно - контейнеры STL не предназначены для этого.

Использование стека процессоров не будет переносимым, и на некоторых платформах может быть невозможно использовать локальные переменные после нажатия чего-либо - вам может понадобиться закодировать все в сборке. (Это вариант, если вам действительно нужно считать каждый последний цикл и вам не нужна переносимость, но убедитесь, что вы используете профилировщик, чтобы убедиться, что он действительно стоит). Нет никакого способа использовать стек другого потока, который будет быстрее, чем контейнер стека.

3 голосов
/ 02 марта 2010

Миннер, вы уверены, что стековые операции являются / могут быть узкими местами нашего приложения? если нет, и я могу сделать ставку, просто используйте std :: stack и забудьте об этом.

2 голосов
/ 02 марта 2010

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

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

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

0 голосов
/ 02 марта 2010

Это зависит от ваших требований. Если вы хотите поместить пользовательский тип данных в стек, вам понадобятся стеки ручной работы

для других говорят, что вы хотите использовать целые числа, символы или указатели объектов, которые вы можете использовать как м { От себя поп } но не испорти это

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...