2013-05-09 13 views
6

Czasami, kiedy uruchomić ten prosty programProgram do planowania wątków systemu Windows jest niesprawiedliwy?

#include <Windows.h> 

DWORD WINAPI ThreadStart(LPVOID) 
{ 
    for (;;) { } 
    return 0; 
} 

int _tmain() 
{ 
    SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); 

    SYSTEM_INFO si; 
    GetSystemInfo(&si); 
    for (DWORD i = si.dwNumberOfProcessors * 2; i > 0; i--) 
    { CloseHandle(CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL)); } 

    Sleep(INFINITE); 
} 

Obserwuję stale nieuczciwej harmonogram wątku, takich jak:

Screenshot

To nie fair na każdym biegu, ale gdy jest, pozostaje niesprawiedliwy przez cały czas trwania aplikacji.

Dlaczego tak się dzieje i jaki jest właściwy sposób, aby tego uniknąć?

+0

Jaki masz tryb procesora? Czy to w rzeczywistości czterordzeniowy, czy też dwurdzeniowy z hyperthreading? – selbie

+0

Również, jaka wersja systemu Windows. Ważne jest również, aby wiedzieć. – selbie

+0

@selbie: 2-rdzeniowy HT (Core i5, to laptop), Windows 8 x64. – Mehrdad

Odpowiedz

1

Niektóre opcje:


Jeśli (i tylko wtedy) używasz systemu Windows 7/8 64-bitowy (a nie 32-bitowa) lub Windows Server 2008 R2 lub później, może całkowicie obejść system harmonogramu i sam zająć się tym, chociaż nie jest to banalne przedsięwzięcie!

To described on MSDN here i jest nazywane Harmonogram trybu użytkownika (UMS).

Częściowy zrzut ekranu ze strony MSDN poniżej - czy to jest dla Ciebie przydatne?

enter image description here

Ponadto, może to być pomysł, aby wyłączyć Hyper-Threading w BIOS (jeśli dotyczy), ponieważ jest jakaś debata, w jaki sposób system Windows rozróżnia logicznych i fizycznych rdzeni. See here for a discussion.

Dostępnych jest również kilka funkcji, takich jak SetThreadAffinityMask() (MSDN here) i SetThreadIdealProcessor(), które mogą pomóc, chociaż osobiście uznałem, że jest to mało popularne. Częściej tak się dzieje, że szkodzę ogólnej przepustowości, niż mu pomagam.

+0

Naprawdę nie mogę wyłączyć HT, ponieważ opcja nie istnieje (ani nie chcę ... ani nie rozumiem, dlaczego to coś zmienia). Niestety ustawienie koligatora lub idealnego procesora również niczego nie zmienia. Wydaje się, że UMS jest masowo przesadzony i nie jestem pewien, czy to pomogłoby, gdyby +1 przynajmniej wspomniał o tym, brzmi, jakby to mogło zadziałać. – Mehrdad

0

To jest strzał w ciemno.

Ale zasugeruję, że Hyper-Threading może nieco przekrzywić wyniki.

Jeśli bios/cmos laptopa umożliwia wyłączenie hiperwątkowości, tak jak robią to niektóre komputery, warto byłoby zobaczyć liczby z kodem działającym tylko na prawdziwych rdzeniach.

I nawet jeśli HT jest przyczyną, dla której jest to niezgodne z wątkiem, to nadal nie wiem, dlaczego to zrobiłoby.

+0

Nie. Nie mogę wyłączyć HT, a jak wspomniałeś, tak naprawdę to nie wyjaśnia w ogóle tego, co otrzymuję. – Mehrdad

1

Zobaczysz to w systemie wieloprocesorowym za każdym razem, gdy wątek nie jest częścią twojego programu. Powiedzmy, że istnieje zadanie systemowe, które uruchamia się na kilka sekund, skanujące zestawy .NET lub coś podobnego. Spowoduje to odrzucenie jednego z twoich wątków z harmonogramu na wiele odcinków czasu, podczas gdy wątki na innych rdzeniach będą nadal działać. Wątek, który został znokautowany, nigdy nie dogoni innych.

+1

To trwa wiecznie. Nie liczę absolutnych cykli, liczę na wykorzystanie procesora, które nie ma nic wspólnego z przeszłą historią wątku. Nie powoduje to również żadnego zadania systemowego itp. Jest to jedyny uruchomiony program, dość oczywisty, łatwy do odczytania z menedżera zadań lub narzędzi tego typu. – Mehrdad

0

Wydaje się, że w moim systemie problem jest łagodzony przez SetThreadAffinityMask. Nadal istnieje pewien brak równowagi, najwyraźniej ze względu na to, że jeden rdzeń ma mniej czasu wolnego niż drugi, ale nie jest tak poważny.

Windows wydaje się niechętny do przesuwania tych wątków między rdzeniami; rzadko zmieniają rdzeń po pierwszej sekundzie.Jeśli nici nie są równomiernie rozmieszczone między rdzeniami, to czasy nitek odzwierciedlają to.

Jest to kod użyłem do wypróbowania masek powinowactwa:

#include <Windows.h> 
#include <stdio.h> 

DWORD WINAPI ThreadStart(LPVOID arg) 
{ 
    DWORD pn1, pn2; 

    printf("Thread %u on processor %u\n", GetThreadId(GetCurrentThread()), pn1 = GetCurrentProcessorNumber()); 

    // The problem comes back if you enable this line 
    // SetThreadAffinityMask(GetCurrentThread(), -1); 

    for (;;) 
    { 
     for (int i = 0; i < 10000; i++); 
     pn2 = GetCurrentProcessorNumber(); 
     if (pn2 != pn1) 
     { 
      pn1 = pn2; 
      printf("Thread %u on processor %u\n", GetThreadId(GetCurrentThread()), pn1); 
     } 
    } 
    return 0; 
} 

int main(int argc, char ** argv) 
{ 
    SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); 

    SYSTEM_INFO si; 
    GetSystemInfo(&si); 

    for (DWORD i = 0; i < si.dwNumberOfProcessors; i++) 
    { 
     SetThreadAffinityMask(CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL), 1 << i); 
     SetThreadAffinityMask(CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL), 1 << i); 
    } 

    Sleep(INFINITE); 
    return 0; 
} 

Takie podejście wydaje się również, aby złagodzić ten problem, choć może nie tak skutecznie:

#include <Windows.h> 
#include <stdio.h> 

DWORD WINAPI ThreadStart(LPVOID arg) 
{ 
    for (;;); 
    return 0; 
} 

int main(int argc, char ** argv) 
{ 
    SYSTEM_INFO si; 
    GetSystemInfo(&si); 

    for (DWORD i = si.dwNumberOfProcessors * 2; i > 0; i--) 
    { 
     CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL); 
    } 

    for (;;) 
    { 
     SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); 
     Sleep(100); 
     SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); 
    } 

    return 0; 
} 

podejrzewam, że my” ponowne spojrzenie na jakiś środek oszczędzania energii zaprojektowany przy założeniu, że wątki o niskim priorytecie nie wymagają sprawiedliwego planowania. (Prawdopodobnie ustawienie wątku lub procesu o niskim priorytecie mówi "Nie obchodzi mnie, ile mam CPU.")