Вопрос по потокам

  • Автор темы Gwyllum
  • Дата начала
Статус
Закрыто для дальнейших ответов.
G

Gwyllum

#1
Суть моей программы - показать, что распараллеливание программы на двухъядерном процессоре способно увеличить скорость вычислений. Программа довольно проста - есть массив из 12 элементов. Программа сперва вычисляет значение каждого. Затем вычисления проводятся с помощью одного процесса. Затем массив разбивается на две части, каждую из которых вычисляют два процесса. Затем на три, на четыре, шесть и двенадцать. Программа замеряет время и строит график.
Как оказалось, время вычисления на двухядернике одним потоком, занимает больше времени, чем обычным кодом. При разбиении на два процесса, это время уменьшается, но при трех - немного увеличивается и т.п. По идее, при вычислении на одноядерном процессоре, разбиение на два потока должно занять больше времени, чем обычное, т.к. тратится дополнительное время на открытие потоков и т.п. Но, результаты приблизительно такие-же.
Подскажите, может в потоках используются какие-то ускоряющие вычисления методы или просто где-то в коде моем ошибка? Пожалуйста, не судите за качество кода - я еще новичок :)

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">код</div></div><div class="sp-body"><div class="sp-content">
C++:
#define ITER 999999
double result[12];
LPVOID atr[2];
DWORD time[7];
DWORD coords[7];
DWORD maxTime;
DWORD WINAPI Calculate(LPVOID value)// function calculate all values
{
int x=11;
int number=(int)atr[1];

for (int j=(int)value*number; j<(int)value*number+number; j++)
{
for (int i=0; i<ITER; i++)
{
result[j]=result[j]+(sqrt(sin((double)x)+cos((double)x ))-sqrt(acos((double)x)+asin((double)x )) );

}
}

return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch(msg)
{
case WM_CREATE:
{

return 0;
}break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;

}break;

case WM_PAINT:
{
InvalidateRect(hwnd, NULL, FALSE);
hdc=BeginPaint(hwnd, &ps);
SelectObject(hdc, penWhite);
SetTextColor(hdc, RGB(255,255,255));
SetBkColor(hdc, RGB(0,0,0));
TextOut(hdc, 20,30,(LPCWSTR)szTitle,6);
MoveToEx(hdc, 50,700, NULL);
LineTo(hdc, 50,50);
MoveToEx(hdc, 50,700, NULL);
LineTo(hdc, 950,700);
for (int i=0; i<7; i++)
{
Ellipse(hdc,i*150+45,693,i*150+45+1 0,703);
}
SelectObject(hdc, penGray);

for (int i=1; i<7; i++)
{
MoveToEx(hdc, i*150+50, 700, NULL);
LineTo(hdc, i*150+50,50);
}
SelectObject(hdc, penGreen);
MoveToEx(hdc, 50, 750-coords[0], NULL);
for (int i=0; i<7; i++)
{

LineTo(hdc, i*150+50,750- coords[i]);
}
EndPaint(hwnd, &ps);
DeleteObject(penWhite);
return 0;
}break;

default: break;
}

return (DefWindowProc(hwnd, msg, wparam, lparam));
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpcmdline, int ncmdshow)
{ HANDLE thread0;
HANDLE thread2[2];
HANDLE thread3[3];
HANDLE thread4[4];
HANDLE thread6[6];
HANDLE thread12[12];
DWORD thread_id[27];
for (int i=0; i<12; i++)
{
result[i]=1;
}
int x=11;
DWORD startTime=GetTickCount();//get start time
for (int j=0; j<12; j++)
{
for (int i=0; i<ITER; i++)
{

result[j]=result[j]+(sqrt(sin((double)x)+cos((double)x ))-sqrt(acos((double)x)+asin((double)x )) );

}
}
time[0]=GetTickCount()-startTime;// calculate time running
atr[0]=LPVOID(1);
atr[1]=LPVOID(12);
for (int i=0; i<12; i++)
{
result[i]=1;
}
startTime=GetTickCount();
thread0=CreateThread(NULL, 0, Calculate, (LPVOID)0, 0, &thread_id[0]);
WaitForSingleObject(thread0, INFINITE);
CloseHandle(thread0);
time[1]=GetTickCount()-startTime;
atr[0]=LPVOID(2);
atr[1]=LPVOID(6);

for (int i=0; i<12; i++)
{
result[i]=1;
}
startTime=GetTickCount();
for (int i=0; i<(int)atr[0]; i++)
{
thread2[i]=CreateThread(NULL, 0, Calculate, (LPVOID)i, 0, &thread_id[i+1]);
}
WaitForMultipleObjects(2, thread2, TRUE, INFINITE);
for (int i=0; i<(int)atr[0]; i++)
{
CloseHandle(thread2[i]);
}
time[2]=GetTickCount()-startTime;
atr[0]=LPVOID(3);
atr[1]=LPVOID(4);
for (int i=0; i<12; i++)
{
result[i]=1;
}
startTime=GetTickCount();
for (int i=0; i<(int)atr[0]; i++)
{ thread3[i]=CreateThread(NULL, 0, Calculate, (LPVOID)i, 0, &thread_id[i+3]);
}
WaitForMultipleObjects(3, thread3, TRUE, INFINITE);
for (int i=0; i<(int)atr[0]; i++)
{
CloseHandle(thread3[i]);
}
time[3]=GetTickCount()-startTime;
atr[0]=LPVOID(4);
atr[1]=LPVOID(3);

for (int i=0; i<12; i++)
{
result[i]=1;
}
startTime=GetTickCount();
for (int i=0; i<(int)atr[0]; i++)
{
thread4[i]=CreateThread(NULL, 0, Calculate, (LPVOID)i, 0, &thread_id[i+6]);
}
WaitForMultipleObjects(4, thread4, TRUE, INFINITE);
for (int i=0; i<(int)atr[0]; i++)
{
CloseHandle(thread4[i]);
}
time[4]=GetTickCount()-startTime;
atr[0]=LPVOID(6);
atr[1]=LPVOID(2);

for (int i=0; i<12; i++)
{
result[i]=1;
}
startTime=GetTickCount();
for (int i=0; i<(int)atr[0]; i++)
{
thread6[i]=CreateThread(NULL, 0, Calculate, (LPVOID)i, 0, &thread_id[i+10]);
}
WaitForMultipleObjects(6, thread6, TRUE, INFINITE);
for (int i=0; i<(int)atr[0]; i++)
{
CloseHandle(thread6[i]);
}
time[5]=GetTickCount()-startTime;
atr[0]=LPVOID(12);
atr[1]=LPVOID(1);

for (int i=0; i<12; i++)
{
result[i]=1;
}
startTime=GetTickCount();
for (int i=0; i<(int)atr[0]; i++)
{
thread12[i]=CreateThread(NULL, 0, Calculate, (LPVOID)i, 0, &thread_id[i+15]);
}
WaitForMultipleObjects(12, thread12, TRUE, INFINITE);
for (int i=0; i<(int)atr[0]; i++)
{
CloseHandle(thread12[i]);
}
time[6]=GetTickCount()-startTime;

maxTime=0;
for (int i=0; i<7; i++)
{
if (maxTime<time[i])
{
maxTime=time[i];
}
}

for (int i=0; i<7; i++)
{
coords[i]=(time[i]*700)/maxTime;
}

hwnd=CreateWindowEx(NULL, WINDOW_CLASS_NAME, szTitle, WS_VISIBLE|WS_OVERLAPPED, 0,0,1024,768, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}
 
O

Odin_KG

#2
Gwyllum
Насколько я помню, от того, что вы создадите в программе второй поток, второе ядро его выполнять не начнет. Т.е. все ваши потоки будут выполняться на одном ядре. А, чтобы подключить к потоку другое ядро, нужно использовать функцию: SetThreadAffinityMask

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

Gwyllum

#3
Gwyllum
Я код не смотрел, но массив или не массив это не важно - лишь бы не было одновременного обращения нескольких потоков в один и тот же байт памяти.
Нет) каждый поток обращается к своей части массива. За подсказку с функцией огромное вам спасибо! :D
 
Z

zeus

#4
Суть моей программы - показать, что распараллеливание программы на двухъядерном процессоре способно увеличить скорость вычислений.
Ваша программа не сможет показать реальные параллельные вычисления))) почему? потому что вы забываете про 0й поток, в котором работает оконная процедура, т.е теоретически 1й поток у вас будет выполняться на одном ядре, а 2й будет делить время с 0м потоком, которые оба выполняются на втором ядре (или наоборот, не суть...)

Пишите консольные программы, в которых не нужно создавать окно, и выводите результаты в файл или на консоль )))
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">пример</div></div><div class="sp-body"><div class="sp-content">
C++:
#include <windows.h>
#define NUM 100

void calc(int *res)
{
...
} // calc

DWORD WINAPI thread(LPVOID val)
{
calc((int *)val);
return 0;
} // thread

int main(void)
{
int res[2][NUM];

HANDLE hThread = CreateThread(NULL, 0, thread, (LPVOID)res[0], 0, NULL);

calc(res[1]);

WaitForSengleObject(hThread, TRUE, INFINITE);
CloseHandle(hThread);

return 0;
} // main
 
G

Gwyllum

#5
Ваша программа не сможет показать реальные параллельные вычисления))) почему? потому что вы забываете про 0й поток, в котором работает оконная процедура, т.е теоретически 1й поток у вас будет выполняться на одном ядре, а 2й будет делить время с 0м потоком, которые оба выполняются на втором ядре (или наоборот, не суть...)
Вы совершенно правы, но, если вычисляется сложная тригонометрическая формула, причем многократно, то время ее вычисления во много раз превышает время работы оконной процедуры. Таким образом, чем больше цикл,тем меньше будет влиять время выполнения оконной процедуры. Тем более, что сперва вычисляются все значения и только потом создается окно :D Но, все равно, спасибо большое, лучше и правда начать с консоли :)
 
G

Gwyllum

#7
Gwyllum
Насколько я помню, от того, что вы создадите в программе второй поток, второе ядро его выполнять не начнет. Т.е. все ваши потоки будут выполняться на одном ядре. А, чтобы подключить к потоку другое ядро, нужно использовать функцию: SetThreadAffinityMask
Прошу прощения, немного не разобрался... SetThreadAffinityMask назначает какому-то ядру процессора поток? А как узнать, на каком ядре нам нужно выполнять процесс?
 
Статус
Закрыто для дальнейших ответов.