Параллельное VS последовательное вычисление
Собсно такую задачку мне и задал препод...
В качестве задания предложено использовать перемножение матрицы A (размера MxN) на вектор B (M). На выходе получится матрица C (размер N), где C(i)=A(i,1)*B(i)+...+A(i,n)*B(i). Кстати N возьмем 10.
Мое видение работы программы такое: в главном цикле последовательно, затем параллельно обрабатываются задания. Задания (матрицы A и B) хранятся в бинарных файлах, откуда перед обработкой считываются в дин. массивы. Задания по сложности варьируются от 100х10 до 5000100х10 с шагом 500000 - итого 10 штук.
Но файл 5000100х10 весит около 210 Метров, корректно ли его считывать в оперативную память (ее 256 Мб)?
Так вот, с последовательным все понятно, суть параллельного - создать 10 потоков, каждый из них посчитает i-й элемент C.
Самое главное, что нужно измерить время, само собой не в секундах :), а учитывая "сложность" задачи - даже не в миллисекундах. Я решил измерять в процессорных тактах (через функцию RDTSC).
Сразу вопрос: если выполнять RDTSC на разных машинах (по производительности), то результаты будут сильно отличаться друг от друга, если да то насколько эффективен такой метод подсчета времени выполнения вычисления?
Ниже приведен кусок моего кода, посвященный параллельной обработке. Есть ли какие замечания к подобной реализации? Обратите внимание на начальный и конечный временные штампы. Как их следует расставить, чтобы условия эксперимента были "максимально чистыми"?. На сколько я понял, полное время подсчета одного задания 10 тредами рассчитывается исходя из конечного штампа последнего (штампы снимаются для каждого и сохраняются в MemoryStream).
{ TDynThread - наследуем от TThread }
constructor TDynThread.Create(Num:byte);
begin
FNum:=Num; {Номер нити}
Inherited Create(false);
Priority:=tpHighest;
end;
procedure TDynThread.Execute;
var
j:LongWord;
begin
for j:=0 to D-1 do
c[FNum]:=c[FNum]+Arr.a[j,FNum]*Arr.b[j];
end;
procedure TDynThread.DoTerminate;
begin
CounterGuard.Acquire; {вход в критическую секцию}
TimeEnd:=DM.RDTSC; {!!Конечный штамп!!}
MemoryStream.Write(TimeEnd,SizeOf(Comp)); {сохраняем}
dec(ActiveThreads); {-1 тред}
if ActiveThreads=0 then
Finished.SetEvent; {если закончен последний то сигналим главному процессу}
CounterGuard.Release; {выход из критической секции}
end;
...
procedure TForm1.ExecParallel;
{Выполнить параллельно}
var
i:byte;
begin
if ActiveThreads>0 then
raise Exception.Create('...');
for i:=1 to 10 do
C:=0; {обнуляем C}
MemoryStream.Clear; {чистим MemoryStream}
ActiveThreads:=10;
Finished.ResetEvent;
TimeBeg:=DM.RDTSC; {!!Начальный штамп!!}
for i:=1 to 10 do
TDynThread.Create(i); {Создаем 10 тредов}
if Finished.WaitFor(20000) <> wrSignaled then {Ждем завешения всех 10}
raise Exception.Create('...');
end;
...
Весь файл не влезит. Насколько я понимаю в этом и суть что весь файл сразу нельзя загрузить. Отсюда и должна появиться разница в эффективностях алгоритмов.
Производительность машин определяет частота. На разных частотах RDTSC будет довать разные значения. Правельно будет замерить частоту процессора в течении секунды. А после перессчитывать на такты. Тогда разные машины можно будет сравнивать.
Помимо прочего разные процессоры выполняют разные операции с разной скоростью в тактах. Тут не сильно разница будет.
Ну такая вот странная задача... надо решить... Или я написал что-то не понятно?:confused:
То есть в сам алгоритм должно входить "считывание на ходу" из файла, например построчно?
А проблемы с блокировками данных при параллельной работе нитей возможны? Какие ограничения на доступ ставить в этом случае? Работаю через FileStream.
Помимо прочего разные процессоры выполняют разные операции с разной скоростью в тактах. Тут не сильно разница будет.
То есть в принципе этим можно пренебречь?
Да в алгоритм стоит внести чтение. Собственно поэтому паролельная обработка и должна дать прирост.
Хотя это эксперемент надо делать.
На это не смогу ответить. Надо сматреть, вообщем случии ограничения должны помочь.
Ты эксперемент поставь и узнаешь вообщем случии можно принебречь.
1. Проблема подсчета времени обычно решается использованием функции GetTickCount (если тебя конечно устроит точность до миллисекунды :) )
2. Проблема с блокировками при доступе к одному файлу из нескольких потоков возникает только при записи в этот файл. Проблем при чтении не должно быть вообще...
В данном случае, для работы с большими объемами данных тебе больше подойдут отображаемые в память файлы - копай в сторону функций CreateFileMapping и MapViewOfFile.
3. Судя по твоему коду, ты присваиваешь вычислительному потоку наивысший приоритет. Тебе не кажется, что это ммм... немного не чесно по отношению к способу с использованием последовательного доступа (если ты, конечно, там также не ставишь высший приоритет)? :)
...кстати от приоритета (normal или highest) как опыт показал не сильно результаты рознятся...
Хм... тогда такое ощущение, что у тебя и правда все упирается в скорость чтения данных с диска.
Дело в том, что мой препод уверен, что при работе с памятью реально может наступить момент, когда параллельное вычисление окажется быстрее...
Сразу скажу, что препод не спец по потокам и вычислениям в них... короче я не могу его в этом убедить...[INDENT]
На самом деле такой файл считывается в память (512Мб) и комп даже выдает результат... только вот вопрос как он туда считывается? Происходит ли свопинг? Если да, то даст ли это тот же результат (ну или подобный), как в случае с последовательным считыванием. Просто дело в том что время параллельного здесь и правда получается меньше. Мне НАДО это объяснить!
Еще такой вопрос: есть программа, написанная по какому-то примеру из учебника, там вычисляется Пи. Есть 2 потока, время замеряется в секундах (функция Time). Если треды сначала запустить один за другим и суммировать их время, а потом запустить их одновременно, то при одновременной работе получается быстрее на 6 секунд (точность до 9-й цифры после запятой)... короче это препод и использует как аргумент...
Если нужно могу выложить исходник этой программы
[/INDENT]
Если нет, то не стоит и заморачиваться на хранение в памяти - все элементы матриц на ходу забить константами (например единицами).
В этом случае будет видна именно разница в скорости вычислений!
В условии задачи сказано, что матрицы нужно считывать из файла?
Если нет, то зачем тебе вообще хранить что-либо?
При перемножении вместо обращения к памяти, используешь константы!
И благополучно замеряешь быстродействие вычислений!
Тебе же нужен хронометраж, а не результат!
По большому счету вам потребуется только 3 переменные (ячейки, а никакие не массивы) - для эмуляции элементов матриц:
A,B =1, а С считаете 2 способами: циклами или параллельно.
то что уже написано 2 месяца назад не есть лишний геморрой ИМХО...
Вероятно, вы сейчас подошли к пределам доступной памяти.
Издержки пошли из-за необходимости подгрузки файла по частям.
Чем не объяснение?
Если же вы хотите выяснить, где в действительности находится точка смены эффективности, то от издержек необходимо избавляться, анализируя только чистые вычисления.
Про геморрой запомню :)
Но здесь же уже выяснили, что такой границы на 1 проце не должно быть... переключения контекстов только добавляют времени работе алгоритма, по сравнению с циклом с цикле...
Разница в отсутствии обращений к файлу (и адресации памяти). Количество итераций вообще не имеет право изменяться для матрицы фиксированного размера.
Выяснили для значений до 1000000х10? А тенденцию проанализировали? Быть может разница как раз сокращается.
Поэтому я и рекомендую проверить экспериментально гораздо большие диапазоны на облегченной задаче.
Для одной матрицы да, но за один тест их анализируется несколько штук, все с разным количеством элементов в строке...
1. на Celeron D-330 2.67GHz - 1024Mb DDR (2 канала по 512) вычисление с заданной точностью 10 раз подряд без потоков проходит быстрее, чем вычисление 10-ю потоками по 1 Пи одновременно...
2. на Pentium 4 (без HT) 1.7GHz -512Mb RIMM (2 канала по 2х128) - все наоборот...
Ну и? Посчитал выборочно соотношение: колеблется от 1,47 до 1,58.
Видимо это потолок, если не увеличить интервал еще.
Так что теперь нужно доказать?
Что на поддержание параллельных потоков расходуются системные ресурсы?