Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

GAMEINATOR forums _ Мастерская: создание модов для S.T.A.L.K.E.R. _ [ТЧ,ЧН,ЗП] Многоядерность

Автор: Stalker_Monolit 10.08.2015, 17:08

Перед прочтением прошу сильно не ругать если что это моя первая тема тут unsure.gif

Перед история поиска или как я нашел строчку - это можно прочесть по желанию.
Начну с того что я как я любитель билдоман и уже вошла в привычку моя поисковая сноровка, и она меня не подвила laugh.gif я начал свой день с того что мне захотелось найти в исходниках X-Ray те строки которые отвечают за использование процессора и я начал эти поиски и они прошли недолго часика так за 4-5 в поиске через Notepad++ я сначала нашел вот эти подозрительные строки.
Клац
// Фунция для тупых требований THQ и тупых американских пользователей
BOOL IsOutOfVirtualMemory()
{
#define VIRT_ERROR_SIZE 256
#define VIRT_MESSAGE_SIZE 512

SECUROM_MARKER_HIGH_SECURITY_ON(1)

MEMORYSTATUSEX statex;
DWORD dwPageFileInMB = 0;
DWORD dwPhysMemInMB = 0;
HINSTANCE hApp = 0;
char pszError[ VIRT_ERROR_SIZE ];
char pszMessage[ VIRT_MESSAGE_SIZE ];

ZeroMemory( &statex , sizeof( MEMORYSTATUSEX ) );
statex.dwLength = sizeof( MEMORYSTATUSEX );

if ( ! GlobalMemoryStatusEx( &statex ) )
return 0;

dwPageFileInMB = ( DWORD ) ( statex.ullTotalPageFile / ( 1024 * 1024 ) ) ;
dwPhysMemInMB = ( DWORD ) ( statex.ullTotalPhys / ( 1024 * 1024 ) ) ;

// Довольно отфонарное условие
if ( ( dwPhysMemInMB > 500 ) && ( ( dwPageFileInMB + dwPhysMemInMB ) > 2500 ) )
return 0;

hApp = GetModuleHandle( NULL );

if ( ! LoadString( hApp , RC_VIRT_MEM_ERROR , pszError , VIRT_ERROR_SIZE ) )
return 0;

if ( ! LoadString( hApp , RC_VIRT_MEM_TEXT , pszMessage , VIRT_MESSAGE_SIZE ) )
return 0;

MessageBox( NULL , pszMessage , pszError , MB_OK | MB_ICONHAND );

SECUROM_MARKER_HIGH_SECURITY_OFF(1)

return 1;
}

Немного посмотрев на них я подумал может это ограничение использование видео памяти но после того как я тут отписался меня в этом переубедили laugh.gif http://www.gameru.net/forum/index.php?s=&showtopic=55777&view=findpost&p=1553341 но главное суть я понял. Немного полистав в глубь x_ray.cpp я увидел вот это:
CODE
#endif
#else // DEDICATED_SERVER
g_dedicated_server = true;
#endif // DEDICATED_SERVER

SetThreadAffinityMask (GetCurrentThread(),1);

// Title window
// Картинка при старте
logoWindow = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_STARTUP), 0, logDlgProc );


HWND logoPicture = GetDlgItem(logoWindow, IDC_STATIC_LOGO);
RECT logoRect;
GetWindowRect(logoPicture, &logoRect);

SetWindowPos (
logoWindow,

И тут я немного погуглил и сказав не может быть начал быстренько собирать сорцы но при этом поменяв цифру 1 на 2 и был приятно удивлен мой гаджет на боковой панели показал усердное использование второго ядра, не долго думая я поставил цифру 0 и попробовав я увидел свое счастье два процессора работают и диспетчер задач показывает что xrEngine использует 70% ЦП УРА!!! Можно танцевать!!! laugh.gif

Как использовать два и более ядер
Немного загуглив я нашел некую информацию как использовать больше ядер но для этого нужно пере-собирать исходники заново потому что я эту цифру 1 рядом с GetCurrentThread в пысовском движке так и не вцепил через HEX-Editor может это кому то другому удастся.

Строка в коде x_ray.cpp
Код
    // Use CPU
    SetThreadAffinityMask        (GetCurrentThread(),1);


Что можно подставить - спасибо другу http://asdandgamer.spaces.ru
Код
The mask is a bitfield: each bit designate a processor. 0 means "no processor". It's not logic.
0x0001 : proc 1
0x0003 : proc 1 and 2
0x000F : proc 1, 2, 3, 4

Так что можно сразу сделать так:
Код
    // Use CPU
    SetThreadAffinityMask        (GetCurrentThread(),0x000F);

Будет использоваться 4 ядра и более.
Полезная ссылка по этим строкам http://www.vsokovikov.narod.ru/New_MSDN_API/Process_thread/fn_process_thread.htm

Автор: cjayho 10.08.2015, 17:58

А вам не кажется, что намного проще закомментарить строчку SetThreadAffinityMask (GetCurrentThread(),1); ?

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

Ps. В смысле возрастет не FPS, а именно скорость их проигрывания, то есть секундная анимация будет отыгрываться за полсекунды на двуядерном проце, четверть секунды на четырехъядерном и так далее.

Автор: Stalker_Monolit 10.08.2015, 18:24

Цитата(cjayho @ 10.08.2015, 17:53) *
Но только учтите, что многоядерности от этого не появится

Почему это? blink.gif Не может мне компьютер наврать что он два процессора не юзал.

Автор: jamakasi 10.08.2015, 18:39

Stalker_Monolit, многоядерность будет но совсем не та что нужна laugh.gif

Автор: Cossack-HD 10.08.2015, 19:25

Снижение time_factor до 0.5 приводит в ощутимому увеличению FPS, что какбэ намекает, что даже в LA с её ЗПподобной "многоядерностью" на одном ядре висит не только графон, но и куча всего типа логики, физики, звука и анимаций (их апдейт замедляется и у ядра остаётся больше времени на отрисовку графонов - вырастает FPS).
Если оставить логику, физику, скрипты и анимации на одном ядре, а рендер почти полностью перенести на другое, то производительность должна улучшиться.

Автор: Stalker_Monolit 10.08.2015, 20:39

Цитата(Cossack-HD @ 10.08.2015, 19:20) *
Снижение time_factor до 0.5 приводит в ощутимому увеличению FPS, что какбэ намекает, что даже в LA с её ЗПподобной "многоядерностью" на одном ядре висит не только графон, но и куча всего типа логики, физики, звука и анимаций (их апдейт замедляется и у ядра остаётся больше времени на отрисовку графонов - вырастает FPS).
Если оставить логику, физику, скрипты и анимации на одном ядре, а рендер почти полностью перенести на другое, то производительность должна улучшиться.

blink.gif А сорцы LA разве уже в сети плавают?
Если это так то можно было это сделать как и разрабы сделали, только дописать пару кусочков кода в нужные dll-ки меняя при этом какой проц будет юзатся.

Автор: jamakasi 10.08.2015, 20:53

Stalker_Monolit, если б было все так просто то уже давным давно бы сделали многопоточку нормальную. Тут слишком многое нужно переделывать в движке. Просто так прописать что вот это должно обсчитываться на этом ядре не получится т.к. потоки должны взаимодействовать друг с другом, ожидать друг друга на блокировках и т.д. . Задача хоть и подсильная но слишком нереально геморойная. Проще пользуйся тем что есть, компы из года в год только растут мощностью поэтому теребонькать на что-то ненужное(многопоточность) бессмысленно, вот 64 битка имеет смысл в игре а многоядерность не особо.К примеру мой комп довольно легко тащит LA(сглаживания не максимальные только и дальность травки).

Автор: Билдоман 11.08.2015, 01:23

Stalker_Monolit,

Плавают, более того, лежат в отдельной теме. http://www.gameru.net/forum/index.php?showtopic=60320&pid=1553432&st=40&#entry1553432

Автор: Stalker_Monolit 11.08.2015, 21:43

Цитата(Билдоман @ 11.08.2015, 01:18) *
Stalker_Monolit,

Плавают, более того, лежат в отдельной теме. http://www.gameru.net/forum/index.php?showtopic=60320&pid=1553432&st=40&#entry1553432

Спасибо а то я думал с другого ресурса качать smile.gif

Автор: Stalker_Monolit 12.08.2015, 17:42

Заметил что сталкер использует расширение функциональности Boost который по копирайту

Цитата
Copyright © 2001-2003
не обновлялся с 2003 гада может попробовать обновить ее? Там же есть и использование потоков и тайминг короче корни для оптимизации тянутся оттуда.

Автор: abramcumner 12.08.2015, 18:05

Stalker_Monolit, думаешь после обновления вызовы потоков, тайминги и корни сами вырастут из воздуха?

А и обнови, кстати. Хорошая задачка для начала.

Автор: STALKER2011x 12.08.2015, 18:27

Цитата(abramcumner @ 12.08.2015, 18:00) *
Stalker_Monolit, думаешь после обновления вызовы потоков, тайминги и корни сами вырастут из воздуха?

Вангую что сначала все поломается и куча времени будут потрачена на то чтобы "вернуть все как было" biggrin.gif

Автор: abramcumner 12.08.2015, 18:32

Совсем нет. Поломается, но не прямо уж все smile.gif Вот луабинд обновить...

Кстати, в ЛА-исходниках не новый разве буст?

Автор: Tron 12.08.2015, 19:06

Вот luabind как раз и поломается. Он кажется зависит от темплейта удаленного в последних версиях
По сути boost используется только luabind'ом

Автор: -StalkMen- 14.08.2015, 12:43

Я обновил boost, только теперь сам не знаю зачем laugh.gif
Разве что опыт biggrin.gif

Автор: abramcumner 14.08.2015, 13:18

-StalkMen-, многоядерность поперла?

Теперь луабинд на очереди smile.gif

Автор: lambdist 14.08.2015, 13:39

Цитата(abramcumner @ 14.08.2015, 14:13) *
Теперь луабинд на очереди

Запилить бы визуальное программирование, подгрузку текстур, продвинутую систему LOD, лицевую анимацию, PhysX или Havok и пожалуй Scaleform.

Автор: STALKER2011x 14.08.2015, 14:01

Цитата(lambdist @ 14.08.2015, 13:34) *
Цитата(abramcumner @ 14.08.2015, 14:13) *
Теперь луабинд на очереди

Запилить бы визуальное программирование, подгрузку текстур, продвинутую систему LOD, лицевую анимацию, PhysX или Havok и пожалуй Scaleform.

А может ещё и удобный WYSIWYG редактор? biggrin.gif Это уже будет totstalkir.gif

Автор: -StalkMen- 14.08.2015, 15:20

Цитата(abramcumner @ 14.08.2015, 13:13) *
Теперь луабинд на очереди

Я их одновременно обновлял rolleyes.gif
С луабиндом гораздо больше заморочек, начиная от переписываниЙ© в xrGame, заканчивая переписыванием скриптов sad.gif

Цитата(abramcumner @ 14.08.2015, 13:13) *
многоядерность поперла?

От обновления boost'а?.. o_O.gif

Цитата(lambdist @ 14.08.2015, 13:34) *
PhysX

Пилю потихоньку wink_old.gif Пока всё нравится, правда мануалов на Русском мало sad.gif
Начал с замены xrCDB (для компиля)

Автор: abramcumner 14.08.2015, 15:24

Цитата(-StalkMen- @ 14.08.2015, 15:15) *
От обновления boost'а?.. o_O.gif

Конечно, его же именно для этого предлагалось обновить smile.gif

Автор: -StalkMen- 14.08.2015, 15:40

Мне вообще кажется, что надо boost вырезать из luabind'а. А вместо него использовать плюшки C++0x.

Что-то вроде такого

Было

Код
// new(1)
template <class T, class P1>
IC    T*        xr_new        (const P1& p1) {
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
    return new (ptr) T(p1);
}
// new(2)
template <class T, class P1, class P2>
IC    T*        xr_new        (const P1& p1, const P2& p2) {
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
    return new (ptr) T(p1,p2);
}
// new(3)
template <class T, class P1, class P2, class P3>
IC    T*        xr_new        (const P1& p1, const P2& p2, const P3& p3) {
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
    return new (ptr) T(p1,p2,p3);
}


Стало
Код
// new()
template <class T, class ... Args>
IC    T*        xr_new(const Args& ... args)
{
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T));
    return new (ptr)T(args...);
}

Автор: abramcumner 14.08.2015, 16:16

Цитата(-StalkMen- @ 14.08.2015, 15:35) *
Мне вообще кажется, что надо boost вырезать из luabind'а. А вместо него использовать плюшки C++0x.

Кто-то уже вырезал. Может Tron?

Автор: Stalker_Monolit 14.08.2015, 16:59

Цитата(-StalkMen- @ 14.08.2015, 15:35) *
// new(1)
template <class T, class P1>
IC    T*        xr_new        (const P1& p1) {
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
    return new (ptr) T(p1);
}
// new(2)
template <class T, class P1, class P2>
IC    T*        xr_new        (const P1& p1, const P2& p2) {
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
    return new (ptr) T(p1,p2);
}
// new(3)
template <class T, class P1, class P2, class P3>
IC    T*        xr_new        (const P1& p1, const P2& p2, const P3& p3) {
    T* ptr    = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
    return new (ptr) T(p1,p2,p3);
}

А ты разве свой проект в debug режиме слаживаешь? Первая строка DEBUG_MEMORY_NAME
Клац
#ifdef DEBUG_MEMORY_NAME
// new(0)
template <class T>
IC T* xr_new ()
{
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T();
}
// new(1)
template <class T, class P1>
IC T* xr_new (const P1& p1) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1);
}
// new(2)
template <class T, class P1, class P2>
IC T* xr_new (const P1& p1, const P2& p2) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2);
}
// new(3)
template <class T, class P1, class P2, class P3>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3);
}
// new(4)
template <class T, class P1, class P2, class P3, class P4>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3,p4);
}
// new(5)
template <class T, class P1, class P2, class P3, class P4, class P5>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3,p4,p5);
}
// new(6)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3,p4,p5,p6);
}
// new(7)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3,p4,p5,p6,p7);
}
// new(8)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7, const P8& p8) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3,p4,p5,p6,p7,p8);
}
// new(9)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7, const P8& p8, const P8& p9) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T), typeid(T).name());
return new (ptr) T(p1,p2,p3,p4,p5,p6,p7,p8,p9);
}
#else // DEBUG_MEMORY_NAME
// new(0)
template <class T>
IC T* xr_new ()
{
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T();
}
// new(1)
template <class T, class P1>
IC T* xr_new (const P1& p1) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1);
}
// new(2)
template <class T, class P1, class P2>
IC T* xr_new (const P1& p1, const P2& p2) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2);
}
// new(3)
template <class T, class P1, class P2, class P3>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3);
}
// new(4)
template <class T, class P1, class P2, class P3, class P4>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3,p4);
}
// new(5)
template <class T, class P1, class P2, class P3, class P4, class P5>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3,p4,p5);
}
// new(6)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3,p4,p5,p6);
}
// new(7)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3,p4,p5,p6,p7);
}
// new(8)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7, const P8& p8) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3,p4,p5,p6,p7,p8);
}
// new(9)
template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>
IC T* xr_new (const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7, const P8& p8, const P8& p9) {
T* ptr = (T*)Memory.mem_alloc(sizeof(T));
return new (ptr) T(p1,p2,p3,p4,p5,p6,p7,p8,p9);
}
#endif // DEBUG_MEMORY_NAME

Автор: -StalkMen- 14.08.2015, 17:21

Stalker_Monolit,
Обычно нет, мой ДеБуг, это Mixed (Копия Release без оптимизации)

Клац

Автор: Stalker_Monolit 14.08.2015, 17:27

Цитата(-StalkMen- @ 14.08.2015, 17:16) *
Stalker_Monolit,
Обычно нет, мой ДеБуг, это Mixed (Копия Release без оптимизации)
Клац

Понятно blush1.gif ты не знаешь что за приоритеты выставлены почти каждому проекту?
R1
Код
Device.seqFrame.Add    (this,REG_PRIORITY_HIGH+0x12345678);

R2
Код
Device.seqFrame.Add    (this,REG_PRIORITY_HIGH+0x12345678);

R3
Код
Device.seqFrame.Add    (this,REG_PRIORITY_HIGH+0x12345678);

И мне кажется есть связь с pure.h в котором вот это:
Клац
#ifndef _PURE_H_AAA_
#define _PURE_H_AAA_

// messages
#define REG_PRIORITY_LOW 0x11111111ul
#define REG_PRIORITY_NORMAL 0x22222222ul
#define REG_PRIORITY_HIGH 0x33333333ul
#define REG_PRIORITY_CAPTURE 0x7ffffffful
#define REG_PRIORITY_INVALID 0xfffffffful

typedef void __fastcall RP_FUNC (void *obj);
#define DECLARE_MESSAGE(name) extern ENGINE_API RP_FUNC rp_##name; class ENGINE_API pure##name { public: virtual void On##name(void)=0; }
#define DECLARE_RP(name) void __fastcall rp_##name(void *p) { ((pure##name *)p)->On##name(); }

DECLARE_MESSAGE(Frame);
DECLARE_MESSAGE(Render);
DECLARE_MESSAGE(AppActivate);
DECLARE_MESSAGE(AppDeactivate);
DECLARE_MESSAGE(AppStart);
DECLARE_MESSAGE(AppEnd);
DECLARE_MESSAGE(DeviceReset);

//-----------------------------------------------------------------------------
struct _REG_INFO {
void* Object;
int Prio;
u32 Flags;
};

ENGINE_API extern int __cdecl _REG_Compare(const void *, const void *);

template <class T> class CRegistrator // the registrator itself
{
friend ENGINE_API int __cdecl _REG_Compare(const void *, const void *);
public:
xr_vector<_REG_INFO> R;
// constructor
struct {
u32 in_process :1;
u32 changed :1;
};
CRegistrator() { in_process=false; changed=false;}

//
void Add (T *obj, int priority=REG_PRIORITY_NORMAL, u32 flags=0)
{
#ifdef DEBUG
VERIFY (priority!=REG_PRIORITY_INVALID);
VERIFY (obj);
for (u32 i=0; i<R.size(); i++) VERIFY( !((R[i].Prio!=REG_PRIORITY_INVALID)&&(R[i].Object==(void*)obj)) );
#endif
_REG_INFO I;
I.Object =obj;
I.Prio =priority;
I.Flags =flags;
R.push_back (I);

if(in_process) changed=true;
else Resort ( );
};
void Remove (T *obj)
{
for (u32 i=0; i<R.size(); i++) {
if (R[i].Object==obj) R[i].Prio = REG_PRIORITY_INVALID;
}
if(in_process) changed=true;
else Resort ( );
};
void Process(RP_FUNC *f)
{
in_process = true;
if (R.empty()) return;
if (R[0].Prio==REG_PRIORITY_CAPTURE) f(R[0].Object);
else {
for (u32 i=0; i<R.size(); i++)
if(R[i].Prio!=REG_PRIORITY_INVALID)
f(R[i].Object);

}
if(changed) Resort();
in_process = false;
};
void Resort (void)
{
qsort (&*R.begin(),R.size(),sizeof(_REG_INFO),_REG_Compare);
while ((R.size()) && (R[R.size()-1].Prio==REG_PRIORITY_INVALID)) R.pop_back();
if (R.empty()) R.clear ();
changed = false;
};
};

#endif

Автор: -StalkMen- 14.08.2015, 17:35

Stalker_Monolit,
Приоритет - выше высокого.

Автор: Tron 14.08.2015, 18:30

Цитата(abramcumner @ 14.08.2015, 16:11) *
Цитата(-StalkMen- @ 14.08.2015, 15:35) *
Мне вообще кажется, что надо boost вырезать из luabind'а. А вместо него использовать плюшки C++0x.

Кто-то уже вырезал. Может Tron?


Не, я этим не занимался. Хотел, но не сделал.
То,что по ссылке выше там же вариадик темплейты, их поддержка только в 13ой студии появилась.
Есть форк луабинда, там С++11 плюшки есть, но собирается и с более ранней студией(с 12 точно)

Added:
Версию с новым луабиндом делал codepoet, но у него что-то там не срослось,ибо он не довел ее

Автор: Tron 23.08.2015, 09:42

Сегодня курил код, нашел механизм работы с потоками. Точнее, я его и до этого видел, но сегодня я прям отладчиком на него попал

Многопоточный рендер там точно есть(ему выделен 1 дополнительный поток, помимо основного).
Даже есть команда для мнгп. окклуюжена

Автор: -StalkMen- 23.08.2015, 15:41

Tron,

Цитата(Tron @ 23.08.2015, 09:37) *
1 дополнительный поток

"X-RAY Secondary thread" Этот?

На сколько я знаю, оба потока работают последовательно sad.gif

Тогда где же тут
Цитата(Tron @ 23.08.2015, 09:37) *
Многопоточный рендер

???

Автор: MegaNub 23.08.2015, 16:27

Цитата(Tron @ 12.08.2015, 20:01) *
По сути boost используется только luabind'ом

В xrNetServer для расчёта CRC?

Автор: Tron 24.08.2015, 05:48

Цитата(-StalkMen- @ 23.08.2015, 15:36) *
Tron,
Цитата(Tron @ 23.08.2015, 09:37) *
1 дополнительный поток

"X-RAY Secondary thread" Этот?

На сколько я знаю, оба потока работают последовательно sad.gif

Тогда где же тут
Цитата(Tron @ 23.08.2015, 09:37) *
Многопоточный рендер

???

в консольных командах есть r2_mt

Но вообще я возможно ошибся

Цитата(MegaNub @ 23.08.2015, 16:22) *
В xrNetServer для расчёта CRC?

можно легко заменить на вариант из zlib

Автор: -StalkMen- 24.08.2015, 14:42

Цитата(Tron @ 24.08.2015, 05:43) *
r2_mt

Код
void CRender::OnFrame()
{
    Models->DeleteQueue            ();
    if (ps_r2_ls_flags.test(R2FLAG_EXP_MT_CALC))    {
        // MT-details (@front)
        Device.seqParallel.insert    (Device.seqParallel.begin(),
            fastdelegate::FastDelegate0<>(Details,&CDetailManager::MT_CALC));

        // MT-HOM (@front)
        Device.seqParallel.insert    (Device.seqParallel.begin(),
            fastdelegate::FastDelegate0<>(&HOM,&CHOM::MT_RENDER));
    }
}

Что-то есть, но Много Поточности нема laugh.gif

Автор: Neo][ 24.08.2015, 16:03

Цитата(-StalkMen- @ 24.08.2015, 16:37) *
Что-то есть, но Много Поточности нема

-StalkMen-, почему такой вывод?

Автор: abramcumner 24.08.2015, 16:31

Три(основной+MT-details+MT-HOM) это разве много? z_lol1.gif

Автор: Neo][ 24.08.2015, 16:40

Цитата(abramcumner @ 24.08.2015, 18:26) *
Три(основной+MT-details+MT-HOM) это разве много?

Ну, только если так смотреть, тогда действительно нема ))

Автор: Tron 24.08.2015, 17:20

Цитата(abramcumner @ 24.08.2015, 16:26) *
Три(основной+MT-details+MT-HOM) это разве много? z_lol1.gif


Цитата(Neo][ @ 24.08.2015, 16:35) *

Цитата(abramcumner @ 24.08.2015, 18:26) *
Три(основной+MT-details+MT-HOM) это разве много?

Ну, только если так смотреть, тогда действительно нема ))


Если так рассуждать, то у меня для вас очень плохие новости..

Во первых Occlusion Query очень дорогая операция, и ее результат будет доступен только через 1-5 кадров, после вызова.
- я лично с этим столкнулся. Да, долго не верил, т.к на моей машине это было незаметно, но запустив на старой машине, я получил прирост почти в 2 раза.
А все из-за чего? - ждем ответа от CPU.
Т.Е в теории это должно повысить производительность

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

Автор: Neo][ 24.08.2015, 17:27

Tron, sarcasm.gif

Автор: -StalkMen- 24.08.2015, 20:09

Цитата(Neo][ @ 24.08.2015, 15:58) *

почему такой вывод?

Код
void             mt_Thread    (void *ptr)    {
    while (true) {
        // waiting for Device permission to execute
        Device.mt_csEnter.Enter    ();

        if (Device.mt_bMustExit) {
            Device.mt_bMustExit = FALSE;                // Important!!!
            Device.mt_csEnter.Leave();                    // Important!!!
            return;
        }
        // we has granted permission to execute
        mt_Thread_marker            = Device.dwFrame;

        for (u32 pit=0; pit<Device.seqParallel.size(); pit++)
            Device.seqParallel[pit]    ();
        Device.seqParallel.clear_not_free    ();
        Device.seqFrameMT.Process    (rp_Frame);

        // now we give control to device - signals that we are ended our work
        Device.mt_csEnter.Leave    ();
        // waits for device signal to continue - to start again
        Device.mt_csLeave.Enter    ();
        // returns sync signal to device
        Device.mt_csLeave.Leave    ();
    }
}

Один поток плодит работЫ, засыпает, другой их делает и засыпает о_О. И так по кругу. (Хоть основных потоков 2, но они работают, как один - последовательно)

Автор: Stalker_Monolit 24.08.2015, 20:12

Цитата(-StalkMen- @ 24.08.2015, 20:04) *
Хоть основных потоков 2, но они работают, как один - последовательно

Интересно если их сделать параллельно что то изменится? unsure.gif

Автор: Tron 24.08.2015, 21:40

Цитата(-StalkMen- @ 24.08.2015, 20:04) *
Один поток плодит работЫ, засыпает, другой их делает и засыпает о_О. И так по кругу. (Хоть основных потоков 2, но они работают, как один - последовательно)

Пояснить логику можешь?
Я вот смотрю и вижу другую логику работы, согласно http://jakeroid.com/critical-section-cpp.html

И да, поток не засыпает, т.к нету вызова http://en.cppreference.com/w/cpp/thread/sleep_for

Автор: -StalkMen- 24.08.2015, 22:08

Tron,
не совсем так однако,
Основной поток плодит работы, да.
Во время рендеринга эти работы обрабатываются вторым потоком. Но из-за хитро*, есть вероятность, что эти работы будут просчитаны в том же потоке.

Автор: Tron 24.08.2015, 22:30

-StalkMen-, я вот не понимаю последние две строчки

Код
// now we give control to device - signals that we are ended our work
        Device.mt_csEnter.Leave    ();
        // waits for device signal to continue - to start again
        Device.mt_csLeave.Enter    ();
        // returns sync signal to device
        Device.mt_csLeave.Leave    ();

Это что-то типа маленькой синхронизации выступает?

Автор: -StalkMen- 24.08.2015, 22:40

Tron,
Да я тоже себе мозг этим сломал.

Код
    // Release start point - allow thread to run
    mt_csLeave.Enter            ();
    mt_csEnter.Leave            ();

Даём шанс 2 потоку стартовать.

Код
    // Release end point - allow thread to wait for startup point
    mt_csEnter.Enter                        ();
    mt_csLeave.Leave                        ();

Лочим. (Если 2 поток робит, то ждём его. Либо Не даём ему стартовать).


Код
    // Ensure, that second thread gets chance to execute anyway
    if (dwFrame!=mt_Thread_marker)            {
        for (u32 pit=0; pit<Device.seqParallel.size(); pit++)
            Device.seqParallel[pit]            ();
        Device.seqParallel.clear_not_free    ();
        seqFrameMT.Process                    (rp_Frame);
    }

Самый laugh.gif Если 2 поток не успел, то делаем его работу сами.

Вроде так.

Автор: Tron 24.08.2015, 22:54

А все понял, картина маслом.
Да походу ты прав

Автор: abramcumner 24.08.2015, 23:07

Вообще-то все немного не так или совсем не так smile.gif
Система простая: основной поток рендерит текущий кадр, во втором обсчитываются данные для следующего.
Второй поток не может не успеть - там же критические секции поставлены. Это значит, что он вообще не работал в этот раз. Без этой проверки будут тупо пропуски обсчета.

Автор: Neo][ 24.08.2015, 23:21

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

Это ЗП?

Автор: -StalkMen- 24.08.2015, 23:31

Neo][,
Я что, я ничего biggrin.gif

Да, ЗП.

Автор: -StalkMen- 25.08.2015, 02:26

Цитата(Stalker_Monolit @ 24.08.2015, 20:07) *
Интересно если их сделать параллельно что то изменится?

Да. Вылетит один из потоков, проверил. Собственно этого следовало ожидать...


Автор: Neo][ 25.08.2015, 12:07

https://images.gameru.net/image/f1442404cc.png.html

Немного изменил для наглядности:
https://images.gameru.net/image/d906ef106f.png.html

Автор: -StalkMen- 25.08.2015, 16:55

Neo][,
Хорошая картинка. Профилировщик?
Но а это ->

Код
if (dwFrame!=mt_Thread_marker)



Автор: refuse 25.08.2015, 20:58

Цитата(abramcumner @ 25.08.2015, 00:02) *
Второй поток не может не успеть - там же критические секции поставлены.

некруто же, платформозависимая хрень (на поверку спинлок, если склероз не изменяет) в эпоху 11-го стандарта!
эй, кто там делал сборку под х64, пришло время заюзать православные примитивы синхронизации! biggrin.gif

Цитата(-StalkMen- @ 25.08.2015, 17:50) *
Профилировщик?

профилировщик может в картинки? blink.gif

Автор: Tron 25.08.2015, 21:21

Цитата(refuse @ 25.08.2015, 20:53) *
эй, кто там делал сборку под х64, пришло время заюзать православные примитивы синхронизации! biggrin.gif

Не вижу смысла тратиться на это, ибо все равно рендер намертво привязан к винде.
Если и буду заниматься МТ, то скорее всего только с фиберами

А то, да ты прав, http://anki3d.org/spinlock/
http://www.swedishcoding.com/wp-content/uploads/2015/03/parallelizing_the_naughty_dog_engine_using_fibers.pdf
Ну,а это еще сильный WIP:

Скрин с использванием в моем движке:

Автор: refuse 25.08.2015, 21:23

Цитата(Tron @ 25.08.2015, 22:14) *
Не вижу смысла тратиться на это, ибо все равно рендер намертво привязан к винде

может стоит тогда начать с отвязывания рендерера от винды? wink_old.gif

Автор: Tron 25.08.2015, 21:25

https://images.gameru.net/image/9689bd517a.png.html

Если конечно у меня еще правильно работает... я сам еще не разобрался... библиотека без документации и примеров

Цитата(refuse @ 25.08.2015, 21:18) *
Цитата(Tron @ 25.08.2015, 22:14) *
Не вижу смысла тратиться на это, ибо все равно рендер намертво привязан к винде

может стоит тогда начать с отвязывания рендерера от винды? wink_old.gif

Ты готов написать новый рендер с 0? - Я, нет. Я уже со своим затрахался вдовль.
Другое дело, заменить его на какую - нибудь либу

Автор: refuse 25.08.2015, 21:39

Цитата(Tron @ 25.08.2015, 22:20) *
Другое дело, заменить его на какую - нибудь либу

есть варианты? smile.gif

Автор: Tron 25.08.2015, 21:47

Цитата(refuse @ 25.08.2015, 21:34) *
Цитата(Tron @ 25.08.2015, 22:20) *
Другое дело, заменить его на какую - нибудь либу

есть варианты? smile.gif

https://github.com/bkaradzic/bgfx

Но автор GL4 версию не развивает активно(в данный момент)
https://github.com/bazhenovc/sigrlinn

Автор: refuse 25.08.2015, 22:16

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

Автор: Neo][ 26.08.2015, 12:47

Цитата(-StalkMen- @ 25.08.2015, 18:50) *
Neo][,
Хорошая картинка. Профилировщик?

-StalkMen-, word smile.gif)

Цитата(refuse @ 25.08.2015, 22:53) *
профилировщик может в картинки?

refuse, профилировщик профилировщику рознь smile.gif

Цитата(-StalkMen- @ 25.08.2015, 18:50) *
Но а это ->
Код
if (dwFrame!=mt_Thread_marker)

Скорее всего workaround для некого побочного эффекта, на вскидку я не смог проследить возможности нарушения условия.

Автор: -StalkMen- 26.08.2015, 22:39

Neo][,

Цитата(Neo][ @ 26.08.2015, 12:42) *

Профилировщик

Второй скрин - откуда инфа о длительности?

Цитата(Neo][ @ 26.08.2015, 12:42) *

Скорее всего workaround для некого побочного эффекта, на вскидку я не смог проследить возможности нарушения условия.

Цитата(-StalkMen- @ 24.08.2015, 22:35) *
Если 2 поток не успел, то делаем его работу сами.

(На мп серваке очень часто юзается)

Автор: Neo][ 27.08.2015, 10:25

Цитата(-StalkMen- @ 27.08.2015, 01:34) *
Второй скрин - откуда инфа о длительности?

-StalkMen-, это сделано специально для наглядности, чтобы исключить вопросы на подобии:
Цитата(-StalkMen- @ 27.08.2015, 01:34) *
Если 2 поток не успел, то делаем его работу сами.


2-й поток не может не успеть, первый поток будет ждать на mt_csEnter
Цитата
Statistic->RenderTOTAL_Real.FrameEnd ();
Statistic->RenderTOTAL.accum = Statistic->RenderTOTAL_Real.accum;
#endif // #ifndef DEDICATED_SERVER
// *** Suspend threads
// Capture startup point
// Release end point - allow thread to wait for startup point
mt_csEnter.Enter ();
mt_csLeave.Leave ();

// Ensure, that second thread gets chance to execute anyway
if (dwFrame!=mt_Thread_marker) {
for (u32 pit=0; pit<Device.seqParallel.size(); pit++)
Device.seqParallel[pit] ();
Device.seqParallel.clear_not_free ();
seqFrameMT.Process (rp_Frame);
}


пока второй не выполнит работу и не освободит эту секцию:

Цитата
Device.seqFrameMT.Process (rp_Frame);

// now we give control to device - signals that we are ended our work
Device.mt_csEnter.Leave ();

Автор: -StalkMen- 27.08.2015, 15:31

Neo][,
wink_old.gif
Хорошо, вопрос, будет ли работать, если сделать так -

Код
void             mt_Thread    (void *ptr)    
{
    return;
    while (true)
    {

Цитата(Neo][ @ 27.08.2015, 10:20) *

пока второй не выполнит работу и не освободит эту секцию:

А то исходя из этого, должен быть dead lock laugh.gif

Автор: Neo][ 27.08.2015, 16:44

Цитата(-StalkMen- @ 27.08.2015, 17:26) *
Хорошо, вопрос, будет ли работать, если сделать так -

-StalkMen-, а почему нет, я что-то пропустил? smile.gif

Вот предложенный тобой случай:
https://images.gameru.net/image/c64661cdd5.png.html

Автор: refuse 29.08.2015, 10:01

Цитата(-StalkMen- @ 27.08.2015, 16:26) *
А то исходя из этого, должен быть dead lock

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

Автор: Flammable 04.11.2015, 13:10

Цитата(Tron @ 24.08.2015, 22:36) *
Это что-то типа маленькой синхронизации выступает?

Это неудачная попытка распараллелить вычисления на два потока в пределах одного кадра: предполагалось, что в каждом кадре первый поток посредством пары критических секций должен запустить параллельно себе второй поток (который выполнит часть задач рендера, обновит маркеры на карте, посчитает физику летящих пуль, обновит звуки и вызовет lua GC). На деле регулярно случались ситуации, когда второй поток вообще не получал времени на протяжении целых серий кадров. Человек, который это все писал, не понял, почему оно так получается, и решил проблему добавлением костыля в виде mt_Thread_marker. В итоге, первый поток регулярно и на протяжении целых серий кадров выполнял работу второго потока, в то время как тот спал. Фикс можно забрать https://github.com/OpenXRay/xray-16/commit/827a6dc5462dd9d15c7cc5e8c5ff033c9c1a6087, если кому-то интересно.

Автор: abramcumner 04.11.2015, 13:33

Цитата(Flammable @ 04.11.2015, 13:16) *
Человек, который это все писал, не понял, почему оно так получается, и решил проблему добавлением костыля в виде mt_Thread_marker.

smile.gif Или, что скорее, ты не понял, что там происходит.

Цитата
В итоге, первый поток регулярно и на протяжении целых серий кадров выполнял работу второго потока, в то время как тот спал.

Чем лучше твое решение: работу выполняет второй поток пока первый спит? Тоже на тоже, только еще расходы на переключение контекста и переходы в ядро и обратно?

Цитата
Фикс можно забрать https://github.com/OpenXRay/xray-16/commit/827a6dc5462dd9d15c7cc5e8c5ff033c9c1a6087, если кому-то интересно.

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

Замеры делались как "было" и как "стало"? На мой взгляд должно тормозить сильнее. Еще и глючить начать.

Автор: Flammable 04.11.2015, 15:43

Цитата(abramcumner @ 04.11.2015, 13:39) *
Чем лучше твое решение: работу выполняет второй поток пока первый спит?

Лучше тем, что задачи второго потока всегда выполняются вторым потоком. Когда fps небольшой, работы у первого потока всегда предостаточно (OnRender + OnFrame), поэтому на практике к снижению производительности это решение не приводит.
Цитата(abramcumner @ 04.11.2015, 13:39) *
Появились глюки из-за несинхронизированного доступа к данным.

Если там и есть глюки, то они есть с самого начала, потому что второй поток как выполнял свои задачи, так и выполняет.
И да, замеры делались, ожидания подтвердились: изменений нет.
upd:
Цитата(abramcumner @ 04.11.2015, 13:39) *
- появилась ошибка синхронизации - нет остановки второго потока, он так и продолжает считать уже посчитанные данные;

Это почему?

Автор: abramcumner 04.11.2015, 16:08

Цитата(Flammable @ 04.11.2015, 15:49) *
изменений нет.

То есть это не фикс.


Цитата(Flammable @ 04.11.2015, 15:49) *
Цитата(abramcumner @ 04.11.2015, 13:39) *
- появилась ошибка синхронизации - нет остановки второго потока, он так и продолжает считать уже посчитанные данные;

Это почему?

Это моя ошибка - спутало название метода Event::Set

Автор: Stalker_Monolit 15.12.2015, 01:26

Всем привет! cool.gif Накопал немного инфы пока был в отключке)))
Вот хочу спросить у опытных людей Атрибут потока МТА при компиляции подымает производительность?
К стате с наступающим новым годом)))

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)