Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / C++ [игнор отключен] [закрыт для гостей] / оптимизация производительности консольного приложения / 25 сообщений из 26, страница 1 из 2
21.08.2013, 09:36
    #38372745
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
Имею консольную программу (хотя это еще вопрос, кто кого имеет :) ), выполняющую математические вычисления над структурами с данными. Каждая структура содержит массивы связей с другими структурами типа vector<Tn>. Четыре типа структур: T1 - 26шт, T2 - 100 шт, T3 - 3 шт, T4 - 1 шт:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
struct T1 
{
    ... some data ...
    std::vector<T2*> links;
}

struct T2 
{
    ... some data ...
    T1 *link;
}

struct T3
{
    ... some data ...
    std::vector<T1*> items;
}

struct T4
{
    ... some data ...
    std::map<int, T3*> items;
}

class CALC
{
public:
    static void Activate(T1* arg);
    static void Activate(T3* arg);
    static void Activate(T4* arg); <-- точка входа
};



Вычисление проводится последовательным перебором структур:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
void CALC::Activate(T4* arg)
{
	for(auto cur=arg->items.begin(),end=arg->items.end(); cur!=end; cur++)
		Activate(cur->second);
}

void CALC::Activate(T3* arg)
{
	for(int i=0,cnt=arg->items.size(); i<cnt; i++)
		Activate(arg->items[i]);
}

void CALC::Activate(T1* arg)
{
	for(int i=0,cnt=arg->links.size(); i<cnt; i++)
		... some calculations ... arg->links[i] ...
	... some calculations ... 
}



И вот, в чем проблема - аналогичная модель работает в аналогичной программе в C# почти в два раза быстрее, чем в C++!!!

Хотелось бы понять, почему так происходит.

Я начал навешивать счетчики на функции Activate и выяснил, что суммарно Activate(T4* arg) работает 22 сек, Activate(T3* arg) 13 сек, а Activate(T1* arg) работает 11 сек. Если учесть, что Activate(T1* arg) производит все вычисления, а Activate(T4* arg) и Activate(T3* arg) ничего посути не делают, кроме перебора элементов вектора и карты, то проблема в скорости доступа к элементам массива (вектора и карты) либо потери на вызове функции. Но это нонсенс - вектора должны работать быстро, а вызовы не могут так сильно тормозить. Либо я что-то упускаю из виду, либо неверно вектора использую (про карту вообще молчу - там 3 элемента и самая большая потеря по времени).

Что думаете? Я ожидаю, что программа на С++ должна работать в два раза быстрее С#, а пока получается наоборот. Нужно решить эту проблему, прежде чем двигаться дальше.

Пока идеи такие: навешать аналогичные счетчики на clr-сборку C# и сравнить результаты.
Поиграться с директивами методов: использовать что-то типа __fastcall
Заменить массивы на простой array[]
Покопаться в настройках компилятора: может собрать exe-типа релис или еще что-то

Накидывайте идеи, буду пробовать...
...
Рейтинг: 0 / 0
21.08.2013, 09:54
    #38372778
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybs,

Приведите полный код программы. Ключи компилятора которые используете.
Так же в вашем случае в виду всего лишь трех элементов это малозначимно, но map != Dictionary.
map - O(log n)
Dictionary - O(const)

Нужен весь код, по приведенному участку не видно где может быть затык.
Кстати поток-то один?
...
Рейтинг: 0 / 0
21.08.2013, 11:58
    #38373013
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
sherzod_andreybs,

Приведите полный код программы. Ключи компилятора которые используете.
Так же в вашем случае в виду всего лишь трех элементов это малозначимно, но map != Dictionary.
map - O(log n)
Dictionary - O(const)

Нужен весь код, по приведенному участку не видно где может быть затык.
Кстати поток-то один?

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
//------------------------------------------------------------------------------
void CALC::Activate(TNeuron* neuron)
{
    TLink *link;
    double sumval = 0;
    for(int i=0,cnt=neuron->Inputs.size(); i<cnt; i++) {
        link = neuron->Inputs[i];
        sumval += link->_Weight * link->NeurSrc->_Outval;
    }

    double value = 0;
    double threshold = neuron->_Threshold;
    double factor = neuron->_Factor;
    switch(neuron->_Activation)
    {
    case AF_LINEAR:
    break;

    case AF_LOGISTIC:
        value = factor * sumval;
        value = 1.0 / (1.0 + exp(-value));

        if(threshold > 0)
            if(value > 1.0-threshold) value = 1;
            else
            if(value < threshold) value = 0;
    break;

    case AF_TANHYP:
    break;
    }
    neuron->_Outval = value;
}
//------------------------------------------------------------------------------
void CALC::Activate(TLayer* layer)
{
    if(layer->_Type == LR_INPUT) return;
    for(int i=0,cnt=layer->Neurons.size(); i<cnt; i++)
        Activate(layer->Neurons[i]);
}
//------------------------------------------------------------------------------
void CALC::Activate(TNet* net)
{
    for(auto cur=net->Layers.begin(),end=net->Layers.end(); cur!=end; cur++)
        Activate(cur->second);
}
//------------------------------------------------------------------------------



вот примерное определение структур:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
struct TNeuron {
    // Данные
    ...
    // Связи (ссылки)
    std::vector<TLink*> Inputs;
    std::vector<TLink*> Outputs;
    TLayer* Layer;
};
struct TLink {
    // Данные
    ...
    // Связи (ссылки)
    TNeuron* NeurSrc;
    TNeuron* NeurDst;
    TNet* Net;
};
struct TLayer {
    // Данные
    ...
    // Связи (ссылки)
    std::vector<TNeuron*> Neurons;
    TNet* Net;
};
struct TNet {
    // Данные
    ...
    // Связи (реальные объекты)
    std::map<int, TLayer*> Layers;
    std::map<int, TNeuron*> Neurons;
    std::map<int, TLink*> Links;
};



замеряем скорость так:

Код: plaintext
1.
2.
for(long count=0;count<1000000;count++)
    CALC::Activate(net);



Проект собран на базе MS Visio 2012, как консольное приложение с дефалтовыми настройками.

Настройки:
[C++]
/Yu"stdafx.h" /GS /analyze- /W3 /Zc:wchar_t /ZI /Gm /Od /Fd"Debug\vc110.pdb" /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\NeuroNet.pch"
[Компилятор]
/OUT:"D:\Private\Mongoose3\Projects\Console\NeuroNet\Debug\NeuroNet.exe" /MANIFEST /NXCOMPAT /PDB:"D:\Private\Mongoose3\Projects\Console\NeuroNet\Debug\NeuroNet.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X86 /INCREMENTAL /PGD:"D:\Private\Mongoose3\Projects\Console\NeuroNet\Debug\NeuroNet.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"Debug\NeuroNet.exe.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1

Приложение однопоточное, но в будущем планирую подключить AMP (Accelerated Massive Parallelism).
...
Рейтинг: 0 / 0
21.08.2013, 12:42
    #38373107
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybs,

Пока видно следующее

По флагам - стоит дебаг режим (это значительно медленнее), отключена оптимизация /Od, включена проверка границ массивов. Видимо это просто дебаг-конфигурация, добавьте релиз-конфигурацию и в ней замеряйте скорость.
По коду - попробуйте заменить std::map на std::unordered_map, используйте префиксный ++ для итераторов.
...
Рейтинг: 0 / 0
21.08.2013, 12:58
    #38373146
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybs, используй Replace Condition With Polimorphysm.
Код: plaintext
1.
2.
3.
4.
class Activate {...};
class Linear : Activate {...};
class Logistic: Activate {...};
class TanHyp : Activate {...};
...
Рейтинг: 0 / 0
21.08.2013, 14:22
    #38373318
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
maytonandreybs, используй Replace Condition With Polimorphysm.
Код: plaintext
1.
2.
3.
4.
class Activate {...};
class Linear : Activate {...};
class Logistic: Activate {...};
class TanHyp : Activate {...};



Обязательно. Вообще я планировал использовать "лямбды". Вот только с производительностью разбирусь...
...
Рейтинг: 0 / 0
21.08.2013, 14:37
    #38373344
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
Я не знаю практически полезных use-case с лямбдами в С++. Всё тки
в С++ надо играть по правилам его использования.
...
Рейтинг: 0 / 0
21.08.2013, 14:43
    #38373356
оптимизация производительности консольного приложения
maytonЯ не знаю практически полезных use-case с лямбдами в С++. Всё тки
в С++ надо играть по правилам его использования.
Вы серьёзно, не знаете где lambdas могут быть полезны?
...
Рейтинг: 0 / 0
21.08.2013, 17:09
    #38373655
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
sherzod_andreybs,

Пока видно следующее

По флагам - стоит дебаг режим (это значительно медленнее), отключена оптимизация /Od, включена проверка границ массивов. Видимо это просто дебаг-конфигурация, добавьте релиз-конфигурацию и в ней замеряйте скорость.
По коду - попробуйте заменить std::map на std::unordered_map, используйте префиксный ++ для итераторов.

Поигрался с компилятором - релис-конфигурация не помогла. Прирост минорный - 5%.

/Yu"stdafx.h" /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Fd"Release\vc110.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Ot /Fp"Release\NeuroNet.pch"

Еще попробую немного поиграть с параметрами оптимизации, но похоже дело в коде. Кстати, как отключить проверку границ массивов?
...
Рейтинг: 0 / 0
21.08.2013, 17:29
    #38373688
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybssherzod_andreybs,

Пока видно следующее

По флагам - стоит дебаг режим (это значительно медленнее), отключена оптимизация /Od, включена проверка границ массивов. Видимо это просто дебаг-конфигурация, добавьте релиз-конфигурацию и в ней замеряйте скорость.
По коду - попробуйте заменить std::map на std::unordered_map, используйте префиксный ++ для итераторов.

Поигрался с компилятором - релис-конфигурация не помогла. Прирост минорный - 5%.

/Yu"stdafx.h" /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Fd"Release\vc110.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Ot /Fp"Release\NeuroNet.pch"

Еще попробую немного поиграть с параметрами оптимизации, но похоже дело в коде. Кстати, как отключить проверку границ массивов?

Так, сказал глупость... :) Настройки я сменил, а тестил старое приложение. Релис-конфигурация дала просто феерические результаты - 2,140 млн вычислений в секунду. В 28 раз быстрее дебаг-версии! Прям не ожидал, что оно на столько повлияет.
...
Рейтинг: 0 / 0
21.08.2013, 18:54
    #38373837
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
sherzod_По коду - попробуйте заменить std::map на std::unordered_map, используйте префиксный ++ для итераторов.

Сделал. Интересное наблюдение - дебаг-версия ускорилась существенно, а релис-версия осталась такой же.
Пробовал использовать доступ к элементам unsorted_map через оператор [], но почему то возвращается null, хотя элементы в массиве есть. Так что оставил ++итератор.
...
Рейтинг: 0 / 0
21.08.2013, 20:01
    #38373887
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
maytonandreybs, используй Replace Condition With Polimorphysm.
Код: plaintext
1.
2.
3.
4.
class Activate {...};
class Linear : Activate {...};
class Logistic: Activate {...};
class TanHyp : Activate {...};



Я попробовал простой динамический вызов функции, указатель которой хранится в каждой структуре. Производительность упала на 7%. Если динамический вызов работает медленнее switch, то нет смысла рассматривать полиморфизм и лямбды, т.к. это еще медленнее.

Переход от структур к классам нежелателен. Хотя попробовать ради эксперимента можно.
...
Рейтинг: 0 / 0
21.08.2013, 21:28
    #38373921
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybs,

Если настолько важна скорость, заинлайньте функции. Они все у вас вызываются в циклах, поэтому выигрыш будет довольно значимый.
...
Рейтинг: 0 / 0
22.08.2013, 00:58
    #38374032
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
sherzod_andreybs,

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

Хорошая мысль, обязательно попробую!
...
Рейтинг: 0 / 0
22.08.2013, 10:45
    #38374222
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybs,

Если в map только три элемента, не лучше ли использовать вектор вместо нее?
...
Рейтинг: 0 / 0
22.08.2013, 11:29
    #38374299
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
sherzod_andreybs,

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

Безуспешно. Даже __forceinline не влияет на производительность.

Еще попробовал связанный со структурой полиморфный класс с одной функцией расчета. Быстрее, чем динамический вызов функции по указателю, но прироста в производительности не дал. Зато можно разносить код по разным функциям.
...
Рейтинг: 0 / 0
22.08.2013, 11:32
    #38374306
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
MasterZivandreybs,

Если в map только три элемента, не лучше ли использовать вектор вместо нее?

Для производительности может и лучше, а для логики работы - хуже, т.к. присутствует достаточно частое обращение к элементам по id и поиск будет тормозить, если использовать вектор. Но ничто не мешает одновременно использовать вектор и карту.
...
Рейтинг: 0 / 0
22.08.2013, 12:07
    #38374384
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
Нашел простое и элегантное (на мой взгляд) решение:

Код: plaintext
1.
2.
3.
4.
5.
typedef void (*pF)(T* arg);
inline void F1(T* arg);
inline void F2(T* arg);
inline void F3(T* arg);
pF F[] = { F1, F2, F3 };



Далее обращаемся к F[0](arg), как к F1(arg) без потери производительности.
...
Рейтинг: 0 / 0
23.08.2013, 10:49
    #38375534
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybsMasterZivandreybs,

Если в map только три элемента, не лучше ли использовать вектор вместо нее?

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


В трёх элементах? Ну ну.
...
Рейтинг: 0 / 0
23.08.2013, 10:53
    #38375540
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybsandreybsпропущено...


Поигрался с компилятором - релис-конфигурация не помогла. Прирост минорный - 5%.

/Yu"stdafx.h" /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Fd"Release\vc110.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Ot /Fp"Release\NeuroNet.pch"

Еще попробую немного поиграть с параметрами оптимизации, но похоже дело в коде. Кстати, как отключить проверку границ массивов?

Так, сказал глупость... :) Настройки я сменил, а тестил старое приложение. Релис-конфигурация дала просто феерические результаты - 2,140 млн вычислений в секунду. В 28 раз быстрее дебаг-версии! Прям не ожидал, что оно на столько повлияет.

Не мудрено, Debug режим вообще ни для чего не предназначен, кроме отладки.
...
Рейтинг: 0 / 0
23.08.2013, 11:03
    #38375555
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybssherzod_По коду - попробуйте заменить std::map на std::unordered_map, используйте префиксный ++ для итераторов.

Сделал. Интересное наблюдение - дебаг-версия ускорилась существенно, а релис-версия осталась такой же.
Пробовал использовать доступ к элементам unsorted_map через оператор [], но почему то возвращается null, хотя элементы в массиве есть. Так что оставил ++итератор.

Скорость debug сборки мерить вообще бессмысленно.

Так тебе теперь хватает скорости в релиз сборке?

Если нет — сори сборку с профайлером, запусти и погляди, где главный тормоз.

Только не мерий ее скорость , и лучше уменьши размерность задачи, что бы программа когда-то кончилась, потому что с профайлером все очень медленно работает.
...
Рейтинг: 0 / 0
23.08.2013, 11:42
    #38375611
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybsНашел простое и элегантное (на мой взгляд) решение:

Код: plaintext
1.
2.
3.
4.
5.
typedef void (*pF)(T* arg);
inline void F1(T* arg);
inline void F2(T* arg);
inline void F3(T* arg);
pF F[] = { F1, F2, F3 };



Далее обращаемся к F[0](arg), как к F1(arg) без потери производительности.


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

Код: plaintext
1.
2.
3.
4.
5.
typedef void (__fastcall* F)(T* arg);
void __fastcall F1(T* arg);
void __fastcall F1(T* arg);
void __fastcall F1(T* arg);
const static FActNeuron ActNeuron[] = { F1, F2, F3 };
...
Рейтинг: 0 / 0
23.08.2013, 11:45
    #38375613
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
MasterZivandreybsпропущено...


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


В трёх элементах? Ну ну.

Элементов может быть чуть больше и тогда линейный поиск усложнится в разы. А вот для перебора через итератор маленького массива (до 12 элементов) разницы вообще нет. Проверил со счетчиками.
...
Рейтинг: 0 / 0
23.08.2013, 11:49
    #38375625
andreybs
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
MasterZivТак тебе теперь хватает скорости в релиз сборке?


Теперь хватает. Сейчас я тестирую разные способы организации структуры блока вычислений, чтобы он не проседал при разрастании логики вычислений. Потом попробую параллельные вычисления AMP. А дальше на полученном движке буду решать реальную задачу.
...
Рейтинг: 0 / 0
23.08.2013, 15:36
    #38376011
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
оптимизация производительности консольного приложения
andreybs, а профилировщик что показывает? Есть возможность подгрузить
задачу на 1-2 минуты объёмом? Маппинг std::map<int, T3*> можно попробовать
заменить на работу с указателями. Всётки мапа больше на целочисленный ID
расчитана. А если у тебя всё in-memory то можно где-то "сократить маршрут".
...
Рейтинг: 0 / 0
Форумы / C++ [игнор отключен] [закрыт для гостей] / оптимизация производительности консольного приложения / 25 сообщений из 26, страница 1 из 2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]