Console MT (Console Multithreaded) Прожект
Я помню когда у меня впервые появилась дуальная система я первым делом написал консольное приложение, которое в двух потоках выводило символы "о" и "х", позже я написал сортировку слияинием, но это другая история. Итак, я решил ничего нового не придумывать, а опробывать (вспомнить) "мягкость" планировщика. Для сравнения я взял
BeOS, Windows 2003 Server и Linux 2.4. Правда в линуксе у меня только консольный режим (поэтому он как бы вне соревнований я просто упомяну о нем). Еще раз напомню что система представляет собой два Pentium III 933 MHz.
КомпетишнВот код для BeOS.
#include
#include
#include
#include
int32 th1(void *data) { while (1) { printf("o"); fflush(stdout); } }
int32 th2(void *data) { while (1) { printf("x"); fflush(stdout); } }
int main(void)
{
status_t exit_value;
int32 data1 = 0;
int32 data2 = 0;
int32 i1 = spawn_thread(th1, "T1", B_REAL_TIME_DISPLAY_PRIORITY, &data1);
int32 i2 = spawn_thread(th2, "T2", B_REAL_TIME_DISPLAY_PRIORITY, &data2);
resume_thread(i1);
resume_thread(i2);
wait_for_thread(i1, &exit_value);
// never reach here
printf("Console MT. version 1.0/BeOS");
return 0;
}
Этот тест в основном нагружает подсистему фреймбуфера если запускается в графическом консольном окне или файловую систему если перенаправить вывод в файл. Эта довольно примитивная по сути программа, но она с первого взгляда дает понять какие приоритеты ставили перед собой разработчики планировщика.
Вот версия для Windows.
#include "stdafx.h" // сомнительный инклуд
#include
#include
#include
#include
unsigned __stdcall ThreadOne( void* pArguments )
{ while (1) { printf("x"); fflush(stdout); } return 0; }
unsigned __stdcall ThreadTwo( void* pArguments )
{ while (1) { printf("o"); fflush(stdout); } return 0; }
int main(int argc, char* argv[])
{
DWORD params;
unsigned ThreadOneId;
unsigned ThreadTwoId;
HANDLE th1 = (HANDLE)_beginthreadex(
NULL, 0, &ThreadOne, NULL, 0, &ThreadOneId );
HANDLE th2 = (HANDLE)_beginthreadex(
NULL, 0, &ThreadTwo, NULL, 0, &ThreadTwoId );
WaitForSingleObject(th1, -1);
return 0;
}
Вообщем для виндовса тема такая: для всех вариантов (однопоточные, многопоточные, libc, напрмер) разный стафф - вот и здесь вместо CreateThread надо писать обернутую _beginthreadex с чем не валится libc от майкрософта. Под линукс код не выложил, но он тоже достаточно прост, используется нативные pthreads.
Тестирование
BeOS
Сначала я тестировал BeOS версию. Потоки создавал с приоритетом B_REAL_TIME_DISPLAY_PRIORITY. Сначала запустил с двумя процессорами потом с одним. С двумя все прозаично, на выводе четкое чередование "хо":
хохохохохохохохо....
С одним немного интереснее. Но сразу видно что кванты выдаются маленькие.
xxxxxoooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxoooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxoooooo
Причем такое чередование в три-четыре линии довольно стабильное.
Потом я перенаправил все это дело в файл и получил приблизительно то же самое. Для двух процессоров было все таже неизменная последовательность "хохохохо". Для однопроцессорного запуска три-четыре линии сменились на четыре-пять. Вообщем не так уж и много, в целом можно сказать что чувствительность у файловой системы к многопоточным приложениям очень высокая.
Я остался доволен. Потом решил немного пришпорить лошадей и понизил приоритет с B_REAL_TIME_DISPLAY_PRIORITY до B_DISPLAY_PRIORITY. Для однопроцессорного запуска линии немного "удлиннились" а для двухпроцессорного получилась такая картина:
ooooooooxoxooxoxxxooxxxxxxoxxxxoxxoooxooooxxxxx
xooooxoxoooxoxooooxxxxxoxoooxoxoxxxxxxooooxooox
oooxoxxoooooxxxxxxoxoooxxxxoxoxxxxxxoxoxxoooooo
oooooooooooooxxxxooxxooooooxxxxxoxoxoooooxxxxxo
xxxoxxxoxooxooooooooooxoooxooxooxxoooxoxoooooxo
xxxoxooooxoxxxxooooooxoooxxxoxooxxxxxoooooxoxoo
oxoxxxxxxooooxxxxxoxxxxxxxxoooooxooxxxoxxooooox
xxxxoxxxoooxoooooooxooxooooooooooxoxoooxxxoxooo
oxxxxxxxxxxxxxxxxxxxoxooooooxoooooxxxxoooxoxoox
xxoxxoxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxoooooxxxxx
xooooooxooooxoxxxxxxxooooooxooooooooooxoxxooooo
xxoooooxooxxxxxxxooooxoooxxxoxxxoxxxoxxxxxoxxxo
oooxoxxxxxxoooooxxoooxxooooooxoooooxooxoxxxxoxx
xoooooxoooxxooooxxxxxxxxxxxxxxxxxxxxxxxxxoooxxx
oxxxxxxoxxxxxxooxoooxxoxxxxxxoooooxoooooxoxxxxx
xoooooxoooxoxxxoxoxxoxxxxxxxxxxxooxoxxoxxxxxooo
oxoxoooxxoooxxxxxoxxxoooxxxoxooxooooooooooxoxxx
oxooxoooooxxxxoxxoooxxoxxxxxxoxxxxxoxxoxoooxoxx
oxxxxxxxxxxxoooooxxxoxooxoxxxoxxxxxxxoooooooooo
ooooooooxoxxoooooxxxxxoooxoooxoooxooooxxxoooxxx
oxxxooxooooooxooooxoxxxooxxoooooxxxoxoxoxxxxxxo
xxxxxoxoooxxxooooooxxxxxoxxxooooxoxxoxxoxxxoooo
Вообщем система на потоки с таким приоритетом, как видно, откличкается неохотно. С файлами та же ситуация.
Windows
У меня на работе стоит Celeron 2.4 GHz. Вот что выдает Windows на этой однопроцессорной машине.
xxxxxoooooooooooooooooxxxxxxxxxxxxxxxxxoooooooo
oooooooooxxxxxxxxxxxxxxxxxoooooooooooooooooxxxx
xxxxxxxxxxxxxoooooooooooooooooxxxxxxxxxxxxxxxxx
ooooooooooooooxxxxxxxxxxxxxxxxxoooooooooooooooo
oxxxxxxxxxxxxxxxxxoooooooooooooooooxxxxxxxxxxxx
xxxxxoooooooooooooooooxxxxxxxxxxxxxxxxxoooooooo
oooooooooxxxxxxxxxxxxxxxxxoooooooooooooooooxxxx
xxxxxxxxxxxxxoooooooooooooooooxxxxxxxxxxxxxxxxx
oooooooooooooooooxxxxxxxxxxxxxxxxxooooooooooooo
Неплохо, скажете вы, мало чем отличается от БиОС. Ан нет. Во первых частота больше, во вторых давайте посмотрим как себя ведет файловая система. Результат после долей секунды такой: Файл размером 39 КБ состоящий из трех частей (по 13 Кб) иксы, нули, иксы. Вообщем синхронный ввод/вывод не в сравнение с BeOS.
На этом месте один мой товарищ http://shvydky.blogspot.com сказал мне: "Конечно будет плохо. Ты ведь не используеш IoCompletionPort!" И предложил такой вариант программы на до-диез:
using System;
using System.Threading;
public class test
{
public static void Method1(object state) {
char ch = (char)state;
while (true) Console.Write(ch);
}
public static void Main() {
ThreadPool.QueueUserWorkItem(new WaitCallback(Method1), 'x');
ThreadPool.QueueUserWorkItem(new WaitCallback(Method1), 'o');
Console.ReadLine();
}
}
Не обдумав все хорошенько я сразу запустил приложение на работе на своем однопроцессорном целероне. Результат еще хуже (чем просто нативные потоки):
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxo
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Во первых, я сначала засомневался если ли вообще здесь IoCompetionPort, так как IoCompletionPort связывается с хэндлом файла, а в коде нигде такого связывания нет. Во вторых, оказывается реализация пула потоков в CLR даже на Windows NT не юзает IoCompletionPort. За детальными комментариями прошу обращаться к:
http://weblogs.asp.net/kavitak/archive/2003/12/15/43581.aspx.
Нужно переписать приложение используя нативные IoCompletionPort, но хочу предсказать что результат все равно будет хуже чем "просто пишущие два потока".
На двухпроцессорных машинах с счастью ставильная последовательность "хохохохо", чего не скажешь о Линуксе. Там даже на двухпроцессорной конфигурации имели место быть разные паттерны. Вообщем забегая вперед скажу что у Линукса самый плохой планировщик. Я не привожу сдесь линукс потому что в текстовой консоли он выводит сволочь как бешеный эти кресты и нули. Вывод в файл же настолько отстает от Windows и BeOS, что я даже не хочу сюда приводить результаты (естественно имеется ввиду мягкость вывода иксов и нулей).
posted by Maxim Sokhatsky #
9:32 AM