Анализ эффективности функционального программирования на языке F# для многоядерных процессоров

Анализ существующих функциональных языков: история, семейства, преимущества. Анализ эффективности параллельного программирования для задачи обработки графического представления фрактальных функций. Программа умножения матриц, обработки изображения.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 12.01.2016
Размер файла 2,5 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

open System.IO

open System.Net

open Microsoft.FSharp.Control.WebExtensions

let getPage (url:string) =

async {

let req = WebRequest.Create(url)

let! res = req.AsyncGetResponse()

use stream = res.GetResponseStream()

use reader = new StreamReader(stream)

let! result = reader.AsyncReadToEnd()

return result

}

Построитель асинхронного потока операций, работает следующим образом. Встретив оператор let! или do!, он начинает выполнять операцию асинхронно, при этом метод, начинающий асинхронную операцию, получит оставшуюся часть блока async в качестве функции обратного вызова. После завершения асинхронной операции переданный callback продолжит выполнение асинхронного потока операций, но, возможно, уже в другом потоке операционной системы (для выполнения кода используется пул потоков). В результате код выглядит так, как будто он выполняется последовательно. То, что может быть с легкостью записано внутри блока async с использованием циклов и условных операторов, достаточно сложно реализуется с использованием обычной техники, требующей описания множества callback-методов и передачей состояния между их вызовами.

MailboxProcessor -- это класс из стандартной библиотеки F#, реализующий один из паттернов параллельного программирования. MailboxProcessor является агентом, обрабатывающим очередь сообщений, которые поставляются ему извне при помощи метода Post. Вся конкурентность поддерживается реализацией класса, который содержит очередь с возможностью одновременной записи несколькими писателями и чтения одним единственным читателем, которым является сам агент [17].

let agent = MailboxProcessor.Start(fun inbox ->

async {

while true do

let! msg = inbox.Receive()

printfn "message received: '%s'" msg

})

2.2 Анализ эффективности программирования на F# для многоядерных процессоров

Для исследования эффективности программирования на языке F# была выбрана одна из базовых операций работы процессора - умножение матриц для чисел с плавающей точкой. Одним из критериев является тот, что умножение матриц легко поддается распараллеливанию. Были выбраны алгоритмы эффективного умножения, которые также способствуют написанию параллельной программы. Для сравнения, аналогичные программы были написаны на императивных языках С++ и C#.

Существует множество алгоритмов перемножения матриц: алгоритм Штрассена, алгоритм Пана, алгоритм Бини, алгоритмы Шёнхаге, алгоритм Копперсмита-Винограда, алгоритмы с использованием теории групп.

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

ленточный алгоритм;

алгоритм Фокса;

алгоритм Кэннона;

Z-преобразование (Morton-order);

В программе для анализа эффективности параллельного программирования использовались алгоритмы Штрассена и Z-преобразования матриц. Рассмотрим их подробней.

Пусть A, B - две квадратные матрицы над кольцом R. Матрица C получается по формуле:

Если размер умножаемых матриц n не является натуральной степенью двойки, мы дополняем исходные матрицы дополнительными нулевыми строками и столбцами. При этом мы получаем удобные для рекурсивного умножения размеры, но теряем в эффективности за счёт дополнительных ненужных умножений.

Разделим матрицы A, B и C на равные по размеру блочные матрицы

где

Однако с помощью этой процедуры нам не удалось уменьшить количество умножений. Как и в обычном методе, нам требуется 8 умножений.

Теперь определим новые матрицы

которые затем используются для выражения Ci,j. Таким образом, нам нужно всего 7 умножений на каждом этапе рекурсии. Элементы матрицы C выражаются из Pk по формулам

Итерационный процесс продолжается n раз, до тех пор пока матрицы Ci,j не выродятся в числа (элементы кольца R). На практике итерации останавливают при размере матриц от 32 до 128 и далее используют обычный метод умножения матриц. Это делают из-за того, что алгоритм Штрассена теряет эффективность по сравнению с обычным на малых матрицах из-за дополнительных копирований массивов [22].

Z-преобразование, или код Мортона (рис.1) впервые был предложен в 1966 году Г.М. Мортоном, он представляет собой пространственную кривую, часто использующуюся в информатике. Благодаря сохраняющему локальность поведению он используется в структурах данных для перевода многомерной информации в одно измерение. Z-значение точки легко вычисляется с помощью бинарного представления значений координат. Как только данные отсортированы в этом порядке, к ним может быть применена любая одномерная структура, например деревья двоичного поиска, B-деревья, списки или хэш-таблицы.

Рис. 1 - Графическое представление Z-преобразования

Для исследования были написаны программы, реализующие умножение матриц на языках С++ и С# с использованием OpenMP и Parallel Extensions, а также на языке F# с использованием асинхронных выражений. Исследование проводилось на двухъядерном процессоре Inter Core 2 Duo, 2.4 Ghz, 3Mb Cache, 3Gb оперативной памяти, среда Visual Studio 2008 Team System, операционная система Microsoft Windows Vista Home Edition, библиотека для параллельных вычислений ParallelExtensions_Jun08CTP, сборка F# 1.9.6.16.

Для исследования были реализованы следующие алгоритмы умножения матриц:

Классический ().

Классический с использованием Z-преобразования.

Штрассена с использованием Z-преобразования.

Результаты измерения времени работы этих алгоритмов для выбранных языков программирования представлены на рисунках 2-6. На рисунке 7 для сравнения был добавлен график зависимости времени выполнения программы от размера матриц для встроенного метода умножения матриц библиотеки Math платформы .NET.

Рис. 2 - График зависимости времени выполнения от размера матриц для классического алгоритма умножения

Рис. 3 - График зависимости времени выполнения от размера матриц для параллельной версии классического алгоритма умножения

Рис. 4 - График зависимости времени выполнения от размера матриц для классического алгоритма умножения с использованием Z-преобразования матриц

Рис. 5 - График зависимости времени выполнения от размера матриц для параллельной версии классического алгоритма умножения с использованием Z-преобразования матриц

Рис. 6 - График зависимости времени выполнения от размера матриц для алгоритма Штрассена с использованием Z-преобразования матриц

Рис. 7 - График зависимости времени выполнения от размера матриц для параллельной версии алгоритма Штрассена с использованием Z-преобразования матриц

После проведенного эксперимента проанализируем полученные результаты:

При обычном и обычном параллельном умножениях .NET языки показали одинаковую производительность, чего и следовало ожидать. Здесь и далее С++ намного опережает их по производительности, что доказывает целесообразность использования для приложений, требующих высокой скорости вычислений компилируемых языков, а F# и C# - интерпретируемые языки. .NET совместимые языки компилируются во второй, кроссплатформенный язык, который называется CIL (Common Intermediate Language). А уже зависимая от платформы среда CLR (Common Language Runtime) компилирует CIL в машинный код, который может быть выполнен на данной платформе.

Умножение с использованием Z-преобразования матриц лучше проходит на С#, чем на F#, но значительно хуже распараллеливается (всего 20-25%).

Массивы располагаются линейно в оперативной памяти, благодаря Z-преобразованию доступ к ним осуществляется последовательно, ниже процент cache-misses, это приводит к уменьшению времени выполнения умножений.

Использование метода Штрассена, как и ожидалось, привело к ускорению работы программы.

В умножении с использованием метода Штрассена F# показывает лучшие результаты, чем C#. Следует полагать, что это происходит благодаря отходу от императивного способа создания новых матриц и использования связывания имен let.

На основе полученных результатов можно сделать вывод, что язык F# позволяет эффективно распараллеливать и решать такую стандартную задачу как умножение матриц, обходя при этом по эффективности другой язык работающий на платформе .NET - C#, но уступая языку С++, работающему с неуправляемой памятью.

3. АНАЛИЗ ЭФФЕКТИВНОСТИ ФУНКЦИОНАЛЬНОГО ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ F# ДЛЯ РЕШЕНИЯ ЗАДАЧИ ОБРАБОТКИ ИЗОБРАЖЕНИЙ ДЛЯ МНОГОЯДЕРНЫХ ПРОЦЕССОРОВ

3.1 Описание задачи

Задача умножения матриц является достаточно абстрактной и не привязана к конкретной проблеме из реального мира, а для исследования эффективности определенного языка необходимо учитывать возможность его применения на практике. В связи с этим для исследования нужно было найти задачу, обладающую следующими свойствами:

Актуальность. Для того чтобы определить язык как эффективный, необходимо убедиться, что он может решать актуальные задачи и получать убедительные результаты.

Реализуемость. Для анализа языка задача не должна быть слишком сложной, но она должна охватывать базовые требования, которые должны обязательно реализовываться в современном языке.

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

Наглядность. Хорошей практикой для описания новых языков является реализация наглядных примеров, так как это способствует восприятию его как альтернативы существующим языкам.

В архиве Microsoft был найден набор таких примеров от 2009-12-09. Описание примеров можно найти в источнике [22]. Из этих примеров для исследования была выбрана задача построения и обработки классического множества Мандельброта.

В математике множество Мандельброта -- это фрактал, определённый как множество точек С на комплексной плоскости, для которых итеративная последовательность

не уходит на бесконечность.

Впервые множество Мандельброта было описано в 1905 году Пьером Фату (Pierre Fatou), французским математиком, работавшим в области аналитической динамики комплексных чисел. Фату изучал рекурсивные процессы вида

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

Фату нашел, что орбита при этом преобразовании показывает достаточно сложное и интересное поведение. Существует бесконечное множество таких преобразований -- своё для каждого значения. Основываясь на своих расчётах, он доказал, что орбита точки, лежащей на расстоянии больше 2 от начала координат, всегда уходит в бесконечность.

Фату никогда не видел изображений, которые мы сейчас знаем как изображения множества Мандельброта, потому что необходимое количество вычислений невозможно провести вручную. Профессор Бенуа Мандельброт был первым, кто использовал для этого компьютер.

Фракталы были описаны Мандельбротом в 1975 году в его книге «Les Objets Fractals: Forme, Hasard et Dimension» («Фрактальные объекты: форма, случайность и размерность») [24].

Фрактал (лат. fractus -- дробленый, сломанный, разбитый) -- сложная геометрическая фигура, обладающая свойством самоподобия, то есть составленная из нескольких частей, каждая из которых подобна всей фигуре целиком. В более широком смысле под фракталами понимают множества точек в евклидовом пространстве, имеющие дробную метрическую размерность (в смысле Минковского или Хаусдорфа), либо метрическую размерность, строго большую топологической [25].

Одной из идей, выросших из открытия фрактальной геометриим, была идея нецелых значений для количества измерений в пространстве. Конечно, мы не можем осознать четырехмерные вещи, хотя Lucky Tesseract и активно работает в этом направлении. Мандельброт назвал нецелые измерения такие как 2.76 фрактальными измерениями. Обыкновенная евклидова геометрия утверждает, что пространство ровное и плоское. Свойства такого пространства такого пространства задают точки, линии, углы, треугольники, кубы, сферы, тетраэдры и т. д.

Мандельброт верил, что действительный ландшафт пространства не ровный и что в нашем мире нет ничего, что было бы совершенно плоским, круглым, то есть все фрактально. Следовательно, объект, имеющий точно 3 измерения невозможен. Поэтому концепция фрактального измерения была нужна для измерения степени неровности вещей.

Первыми открытыми фракталами были т.н. детерминированные фракталы. Их отличительной чертой является свойство самоподобия, обусловленное особенностями метода их генерации.

Такие фракталы называются классическими, геометрическими фракталами или линейными фракталами. Эти фракталы обычно формируются начиная с инициатора -- фигуры, к которой применяется определенный основной рисунок. Во всех детерминированных фракталах, само-подобие проявляется на всех уровнях. Это значит, что независимо от того насколько вы приближаете фрактал, вы увидите все тот же узор. Для сложных фракталов, которые будут рассмотрены позже, это не так. Детерминистские фракталы образуются в процессе, называемом итерацией, которая применяет основной рисунок к инициатору, после чего применяет его к результату и так далее. Из примеров можно выделить решетку Серпинского (рис. 8), фрактал Серпинского (рис. 9),крест Коха (рис. 10), фрактал Мандельброта (рис. 11), колбасу Минковского (рис. 12), пятиугольник Дарера (рис. 13), кривую Гильберта (рис 14), кривую Дракона (рис 15). Их фрактальные размерности находятся в пределах от 1.26 до 2.0.

Большая часть встречающихся сегодня фракталов не являются детерминированными. Они не линейны и не собраны из повторяющихся геометрических форм. Такие фракталы называются сложными. Если увеличить малую область фрактала до размера большей области то получатся два изображения, которые будут очень похожи в деталях, но они не будут полностью идентичными.

Детерминистские фракталы являются линейными, тогда как сложные фракталы таковыми не являются. Будучи нелинейными, эти фракталы генерируются нелинейными алгебраическими уравнениями.

Из самых известных недетерминированных фракталов можно выделить множества Мандельброта (рис. 16) и Жулиа (рис. 17).

Большинство фракталов, которые можно найти на сегодняшний день, представлены в богатой цветовой схеме. Возможно, фрактальные изображения получили такое большое эстетическое значение именно благодаря этой особенности. После того, как уравнение посчитано, начинается применение цвета. Если результаты остаются стабильными, или колеблются вокруг определенного значения, точка обычно принимает черный цвет. Если значение на том или ином шаге стремится к бесконечности, точку закрашивают в другой цвет, может быть в синий или красный. Во время этого процесса, компьютер назначает цвета для всех скоростей движения.

Фракталы находят все большее и большее применение в науке. Основная причина этого заключается в том, что они описывают реальный мир иногда даже лучше, чем традиционная физика или математика. Вот несколько примеров:

Наиболее полезным использованием фракталов в компьютерной науке является фрактальное сжатие данных. В основе этого вида сжатия лежит тот факт, что реальный мир хорошо описывается фрактальной геометрией. При этом, картинки сжимаются гораздо лучше, чем это делается обычными методами (такими как jpeg или gif). Другое преимущество фрактального сжатия в том, что при увеличении картинки, не наблюдается эффекта пикселизации (увеличения размеров точек до размеров, искажающих изображение). При фрактальном же сжатии, после увеличения, картинка часто выглядит даже лучше, чем до него.

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

При помощи фракталов также можно смоделировать языки пламени.

Пористые материалы хорошо представляются в фрактальной форме в связи с тем, что они имеют очень сложную геометрию. Это используется в нефтяной науке.

Для передачи данных на расстояния используются антенны, имеющие фрактальные формы, что сильно уменьшает их размеры и вес.

Фракталы используются для описания кривизны поверхностей. Неровная поверхность характеризуется комбинацией из двух разных фракталов.

Биосенсорные взаимодействия и биения сердца в медицине.

Моделирование хаотических процессов, в частности при описании моделей популяций в биологии.

Применение фракталов в стеганографии и для шифрования данных.

Сегодня Мандельброт и другие ученые, такие как Клиффорд А. Пикковер (Clifford A. Pickover), Джеймс Глейк (James Gleick) или Г. О. Пейтген (H.O. Peitgen) пытаются расширить область фрактальной геометрии так, чтобы она могла быть применена практически ко всему в мире, от предсказания цен на рынке ценных бумаг до совершения новых открытий в теоретической физике [26].

Тем не менее и сама задача построения фракталов не завершилась а продолжила развитие в 3D-масштабах. Далее приведены примеры изображений, построенных с использованием фракталов.

Рис. 18 - 3D-изображения, построенные при помощи фракталов

Анализ эффективности параллельного программирования на F# для задачи обработки графического представления фрактальных функций

Задача графической реализации фрактального множества была выбрана не случайно, ведь, как и в большинстве задач по обработке изображений, в данной присутствует возможность для распараллеливания вычислений по данным. В программе было использовано распараллеливание по независимым рядам пикселей, что позволило равномерно распределить нагрузку между ядрами процессоров.

Для исследования эффективности использования языка F#, было реализовано 2D-изображение множества Мандельброта на 256 цветах в окне Windows. В качестве дополнительного функционала были реализованы такие функции как масштабирование окна, приближение и удаление картинки, восстановление первоначального приближения, перемещение по изображению.

Для тестирования было использовано следующее оборудование:

Intel Core 2 Duo, 2.4 Ghz, 3Mb Cache, 3Gb оперативной памяти, среда Visual Studio 2010 Ultimate, операционная система Microsoft Windows Vista Home Edition 32x.

QuadCore Intel Core i7, 2.6 Ghz, 6Mb Cache, 6Gb оперативной памяти, среда Visual Studio 2010 Ultimate, операционная система Microsoft Windows 7 Home Premium 64x.

Для тестирования исходное изображение десятикратно увеличивалось и сравнивалось время его перерисовки для языков F#, C++/CLR, C#.

Так выглядит окно программы после применения нескольких увеличений.

Рис. 19 - Окно программы Interactive Fractal

В результате исследования были представлены несколько графиков, которые отображают количество обработанных пикселей в миллисекунду на 10 шагах приближения множества Мандельброта.

Результаты тестирования на первом оборудовании:

Рис. 20 - Скорость обработки пикселей на десяти шагах приближения для последовательной версии программы

Рис. 21 - Скорость обработки пикселей на десяти шагах приближения для параллельной версии программы

Рис. 22 - Коэффициент ускорения работы программы на десяти шагах приближения

Результаты тестирования на втором оборудовании:

Рис. 23 - Скорость обработки пикселей на десяти шагах приближения для последовательной версии программы

Рис. 24 - Скорость обработки пикселей на десяти шагах приближения для параллельной версии программы

Рис. 25 - Коэффициент ускорения работы программы на десяти шагах приближения

По полученным результатам можно сделать c выводы:

Результаты отличаются для двух- и четырехъядерных процессоров. Лучшие результаты язык F# показывает при тестировании параллельной версии программы на четырех ядрах.

Использование языка F# не дает выигрыша в скорости выполнения программы.

Так как все языки компилируются в CLI, результаты работы программы обработки изображения примерно одинаковы.

F# показывает лучший коэффициент ускорения на многоядерном процессоре, чем другие языки

В ряде тестов ускорение при работе нескольких ядер превышало их количество.

Проанализируем код программ для получения объяснения результатов эксперимента.

С++

RenderImageData^ rrd = gcnew RenderImageData(position, width, height);

if (parallel)

{

ParallelOptions ^options = gcnew ParallelOptions();

options->CancellationToken = cancellationToken;

Parallel::For(0, rrd->ImageHeight, options, gcnew Action<int>(rrd, &RenderImageData::RenderRow));

}

else

{

for (int row = 0; row < rrd->ImageHeight; row++)

{

cancellationToken.ThrowIfCancellationRequested();

rrd->RenderRow(row);

}

}

Data = gcnew array<Byte>(Pixels);

void RenderRow(int row)

{

double initialY = row * RowToYTranslation + Top;

for (int col = 0, currentPixelIndex = row * ImageWidth; col < ImageWidth; col++, currentPixelIndex++)

{

Complex c = *(gcnew Complex(col * ColToXTranslation + Left, initialY));

Complex z = c;

for (int iteration = 0; iteration < MaxIterations; iteration++)

{

if (z.Magnitude > 4)

{

Data[currentPixelIndex] = iteration;

break;

}

z = (z * z) + c;

}

}

}

Видно, что основные вычисления происходят в циклах по высоте изображения, ширине и количеству итераций для определения цвета точки. Для осуществления распараллеливания используется конструкция for класса Parallel библиотеки TPL. Также очевиден и недостаток такой реализации - все процессы обращаются к общей структуре, что теоретически допускает возможность конфликтов между записывающими потоками. Из неудобств написания программы нужно выделить отсутствие поддержки IntelliSense, необходимость инициализации и выделения памяти массиву данных до его использования.

C#

if (parallelRendering)

{

var options = new ParallelOptions { CancellationToken = cancellationToken };

Parallel.For(0, imageHeight, options, row =>

{

double initialY = row * rowToYTranslation + top;

fixed (byte* ptr = data)

{

byte* currentPixel = &ptr[row * imageWidth];

for (int col = 0; col < imageWidth; col++, currentPixel++)

{

Complex c = new Complex(col * colToXTranslation + left, initialY);

Complex z = c;

for (int iteration = 0; iteration < maxIterations; iteration++)

{

if (z.Magnitude > 4)

{

*currentPixel = (byte)iteration;

break;

}

z = (z * z) + c;

}

}

}

});

}

else

{

for (int row = 0; row < imageHeight; row++)

{

cancellationToken.ThrowIfCancellationRequested();

double initialY = row * rowToYTranslation + top;

fixed (byte* ptr = data)

{

byte* currentPixel = &ptr[row * imageWidth];

for (int col = 0; col < imageWidth; col++, currentPixel++)

{

Complex c = new Complex(col * colToXTranslation + left, initialY);

Complex z = c;

for (int iteration = 0; iteration < maxIterations; iteration++)

{

if (z.Magnitude > 4)

{

*currentPixel = (byte)iteration;

break;

}

z = (z * z) + c;

}

}

}

};

}

Этот же способ был реализован и на C#, что в конечном итоге приводит к тем же проблемам. Кроме того, используется unsafe(небезопасный) код с указателями. Из-за того большая часть кода повторяется дважды, программа становится громоздкой и неудобной для повторного использования. Повторное написание кода вызвано использованием многих переменных класса, т.е. состояния программы.

F#

let rec drawBit (z : Complex) c curIter =

match curIter with

x when x = maxIterations ->byte 0

|_ ->

match z with

x when x.Magnitude > 4.0 -> byte curIter

|_ -> drawBit (z*z+c) c (curIter+1)

let getbyte initialY j =

cancellationToken.ThrowIfCancellationRequested()

let c = new Complex((float j) * colToXTranslation + left, initialY)

drawBit c c 0

let data =

if parallelRendering then

Array.Parallel.init imageHeight (fun i -> Array.init newImageWidth (fun j -> getbyte ((float i) * rowToYTranslation + top) j))

else

Array.init imageHeight (fun i -> Array.init newImageWidth (fun j -> getbyte ((float i) * rowToYTranslation + top) j))

Версия на F# имеет следующие отличия:

Отсутствуют операторы присваивания, что автоматически устраняет проблему конфликта между процессами

Циклы заменяются рекурсией, которая, в силу того, что она «хвостовая» компилируется обратно в цикл

Легче производить функциональную декомпозицию, код становится короче

Параллельная версия отличается от последовательной одним словом

Отсутствует необходимость инициализировать массив до его заполнения, так как эти операции можно выполнить одновременно

Результаты эксперимента несколько различаются для 2х и 4х ядерного процессора, наиболее эффективно проявляет себя параллельная версия программы на 4х ядерном процессоре

В этом эксперименте также проявился эффект сверхлинейного ускорения - скорость работы программы при подключении двух и четырех ядер увеличивалась более чем в два и четыре раза соответственно. Это объясняется использованием общего кэша L2 на процессоре Core 2 Duo и L3 на процессоре i7. Можно выделить следующие преимущества систем с общим кэшем:

Увеличивается коэффициент использования кэша.

Уменьшается сложность согласования кэшей.

Уменьшается количество промахов кэша.

Уменьшается расход памяти - одни и те же данные для нескольких процессоров записываются единожды.

Уменьшается нагрузка на шину данных - проблемы доступа к данным разрешаются на уровне кэша [27].

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

ЗАКЛЮЧЕНИЕ

В результате проделанной работы можно утверждать, что язык F# является эффективным .NET языком программирования для реализации параллельных вычислений или параллельной обработки данных. В эксперименте с умножением матриц по алгоритму Штрассена с использованием Z-порядка реализация на F# оказалась эффективней другого .NET языка C#. В эксперименте с оконным приложением для обработки изображения недетерминированного фрактала язык показал лучший коэффициент ускорения при переходе с одного ядра на четыре, при этом обеспечивая безопасный доступ потоков различных ядер к общим данным. Кроме того, возможности асинхронной работы потоков, интеграции с другими .NET приложениями, интерактивного выполнения кода, статическая типизация, функциональная основа делают его незаменимым инструментов в руках профессионального разработчика. Поддержка своих механизмов и библиотек .NET для параллельного программирования для многоядерных процессоров позволяет F# успешно конкурировать с другими мощными языками поддерживающими возможности параллельной разработки.

ЛИТЕРАТУРА

1. http://www.ict.edu.ru/ft/005135//ch3.pdf

2. http://ru.wikipedia.org/wiki/Язык_функционального_программирования

3. http://ru.wikipedia.org/wiki/Препроцессор

4. http://ru.wikipedia.org/wiki/Lisp

5. http://progopedia.ru/language/ml/

6. http://oops.math.spbu.ru/~msk/languages.functional.html

7. http://en.wikipedia.org/wiki/Hope_(programming_language)

8. http://progopedia.ru/language/miranda/

9. http://ru.wikipedia.org/wiki/Haskell

10. http://www.intuit.ru/department/pl/haskel98/1/

11. http://ru.wikipedia.org/wiki/Охрана_(программирование)

12. http://www.uchi-it.ru/7/2/3.html

13. http://www.rsdn.ru/article/funcprog/fp.xml

14. http://ru.wikipedia.org/wiki/Функциональное_программирование

15. http://msdn.microsoft.com/ru-ru/library/bb669144.aspx

16. http://habrahabr.ru/blogs/net/71656/

17. http://fprog.ru/2010/issue5/maxim-moiseev-et-al-fsharp-intro/

18. http://www.realcoding.net/article/view/2764

19. http://www.dartmouth.edu/~rc/classes/intro_mpi/parallel_prog_compare.html

20. http://msdn.microsoft.com/en-us/library/dd460693.aspx

21. http://ru.wikipedia.org/wiki/Алгоритм_Штрассена

22. http://blogs.msdn.com/b/pfxteam/archive/2009/12/09/9934811.aspx

23. http://ru.wikipedia.org/wiki/Множество_Мандельброта

24. http://ru.wikipedia.org/wiki/Фрактал

25. http://www.chaostarantula.narod.ru/ToC/1.htm

26. http://drdobbs.com/embedded-systems/196902836

27. Tomas Petricek,Jon Skeet Functional Programming for the Real World/Manning/2009

28. Сошников Д.В. - лекции (“Функциональное программирование”)/2008/Lect1, видео (“F# : Новый язык программирования .NET”)/Платформа/ 2009

29. Тим Мэттсон, Андрей Чернышев Введение в технологии параллельного программирования /Intel® Software Network/2009

30. Don Syme, Adam Granicz, and Antonio Cisternino Expert F#/Apress/2010

ПРИЛОЖЕНИЕ 1

Тексты программ на F#

А. Программа для умножения матриц

#light//включение light-синтаксиса

open System

open System.Threading

/// Измерение времени выполнения функции

let Time f =

let timer = System.Diagnostics.Stopwatch.StartNew ()

let y = f ()

printfn "Time taken = %O" timer.Elapsed

y

let MatrixMultiplyDemo () =

/// Обыкновенное умножение

let Multiply (A: float[,]) (B: float[,])(C: float[,]) =

let n = Array2D.length1 A

for i = 0 to (n-1) do

for j=0 to n-1 do

for k=0 to n-1 do

C.[i,j] <- C.[i,j] + A.[i,k] * B.[k,j]

C

/// Простое умножение

let MultiplyX (A: matrix) (B: matrix) =

(fun x y -> x * y) A B //анонимная функция

/// Быстрое умножение с использованием алгоритма Parallel-FX

let PFXMultiply (A: float[,]) (B: float[,]) (C: float[,]) =

let n = Array2D.length1 A

let RowTask i =

for j=0 to n-1 do

for k=0 to n-1 do

C.[i,j] <- C.[i,j] + A.[i,k] * B.[k,j]

//далее идет инструкия из Parallel Extensions .NET

Parallel.For(0, (n-1), new Action<int>(RowTask))

C

///Создание Z-матриц

let CreateZMatrix (A: float[,]) (B: float[,]) (C: float[,] ) =

let n = Array2D.length1 A

let zMaskSize = int(Math.Log(float(n*n),float(2)))

let DecalcZ z =

let mutable z1 = z

let mutable i = 0

let mutable j = 0

let mutable bit = 1

while (bit <= z1) do

j <- j ||| (z1 &&& bit)

z1 <- z1 >>> 1

i <- i||| (z1 &&& bit)

bit <- bit <<< 1

(i,j) //возвращается tuple

let f i (M: float[,]) =

let (i1,j1)=DecalcZ i

M.[i1,j1]

let zA=Array.init (1<<<zMaskSize) (fun i -> f i A)

let zB=Array.init (1<<<zMaskSize) (fun i -> f i B)

let mutable zC=Array.init (1<<<zMaskSize) (fun i -> f i C)

(zA,zB,zC) //массив zC теперь изменяемый

///параллельное Z-умножение

let PZMatrixMultiply (zA:float[]) (zB:float[]) (zC:float[]) =

let n=Array.length zA

let ZCode = 0

let MaskBits = int(Math.Log(float(n),float(2)))

let rec zMultiply (aZCode,aMaskBits) (bZCode,bMaskBits) (cZCode,cMaskBits)=

if aMaskBits >=2 then

let GetSubcells (zCode, maskBits) = //разбиение на 4 ячейки

let subMaskBits = maskBits - 2

let zI =

[|

(zCode , subMaskBits)

(zCode ||| (1<<<subMaskBits), subMaskBits)

(zCode ||| (2<<<subMaskBits), subMaskBits)

(zCode ||| (3<<<subMaskBits), subMaskBits)

|]

zI

let c1sub = GetSubcells (aZCode,aMaskBits)

let c2sub = GetSubcells (bZCode,aMaskBits)

let cRsub = GetSubcells (cZCode,aMaskBits)

zMultiply c1sub.[0] c2sub.[0] cRsub.[0]

zMultiply c1sub.[1] c2sub.[2] cRsub.[0]

zMultiply c1sub.[0] c2sub.[1] cRsub.[1]

zMultiply c1sub.[1] c2sub.[3] cRsub.[1]

zMultiply c1sub.[2] c2sub.[0] cRsub.[2]

zMultiply c1sub.[3] c2sub.[2] cRsub.[2]

zMultiply c1sub.[2] c2sub.[1] cRsub.[3]

zMultiply c1sub.[3] c2sub.[3] cRsub.[3]

else

zC.[cZCode] <- zA.[aZCode] * zB.[bZCode] + zC.[cZCode]

let PzMultiply (aZCode,aMaskBits) (bZCode,bMaskBits) (cZCode,cMaskBits)=

if aMaskBits >=2 then

let GetSubcells (zCode, maskBits) =

let subMaskBits = maskBits - 2

let zI =

[|

(zCode , subMaskBits)

(zCode ||| (1<<<subMaskBits), subMaskBits)

(zCode ||| (2<<<subMaskBits), subMaskBits)

(zCode ||| (3<<<subMaskBits), subMaskBits)

|]

zI

let c1sub = GetSubcells (aZCode,aMaskBits)

let c2sub = GetSubcells (bZCode,aMaskBits)

let cRsub = GetSubcells (cZCode,aMaskBits)

let tasks = [//разбиение на асинхронные потоки

async

{

zMultiply c1sub.[0] c2sub.[0] cRsub.[0]

zMultiply c1sub.[1] c2sub.[2] cRsub.[0]

}

async

{

zMultiply c1sub.[0] c2sub.[1] cRsub.[1]

zMultiply c1sub.[1] c2sub.[3] cRsub.[1]

}

async

{

zMultiply c1sub.[2] c2sub.[0] cRsub.[2]

zMultiply c1sub.[3] c2sub.[2] cRsub.[2]

}

async

{

zMultiply c1sub.[2] c2sub.[1] cRsub.[3]

zMultiply c1sub.[3] c2sub.[3] cRsub.[3]

}

]

tasks |> Async.Parallel |> Async.RunSynchronously |>ignore

else

zC.[cZCode] <- zA.[aZCode] * zB.[bZCode] + zC.[cZCode]

//вычисление на последнем уровне

PzMultiply (ZCode,MaskBits)(ZCode,MaskBits)(ZCode,MaskBits)

zC

///простое Z-умножение

let ZMatrixMultiply (zA:float[]) (zB:float[]) (zC:float[]) =

let n=Array.length zA

let ZCode = 0

let MaskBits = int(Math.Log(float(n),float(2)))

let rec zMultiply (aZCode,aMaskBits) (bZCode,bMaskBits) (cZCode,cMaskBits)=

if aMaskBits >=2 then

let GetSubcells (zCode, maskBits) =

let subMaskBits = maskBits - 2

let zI =

[|

(zCode , subMaskBits)

(zCode ||| (1<<<subMaskBits), subMaskBits)

(zCode ||| (2<<<subMaskBits), subMaskBits)

(zCode ||| (3<<<subMaskBits), subMaskBits)

|]

zI

let c1sub = GetSubcells (aZCode,aMaskBits)

let c2sub = GetSubcells (bZCode,bMaskBits)

let cRsub = GetSubcells (cZCode,cMaskBits)

zMultiply c1sub.[0] c2sub.[0] cRsub.[0]

zMultiply c1sub.[1] c2sub.[2] cRsub.[0]

zMultiply c1sub.[0] c2sub.[1] cRsub.[1]

zMultiply c1sub.[1] c2sub.[3] cRsub.[1]

zMultiply c1sub.[2] c2sub.[0] cRsub.[2]

zMultiply c1sub.[3] c2sub.[2] cRsub.[2]

zMultiply c1sub.[2] c2sub.[1] cRsub.[3]

zMultiply c1sub.[3] c2sub.[3] cRsub.[3]

else

zC.[cZCode] <- zA.[aZCode] * zB.[bZCode] + zC.[cZCode]

zMultiply (ZCode,MaskBits)(ZCode,MaskBits)(ZCode,MaskBits)

zC

///Умножение с использование метода Штрассена

let StrassenMultiply (zA:float[]) (zB:float[]) (zC:float[]) =

let n=Array.length zA

let ZCode = 0

let MaskBits = int(Math.Log(float(n),float(2)))

let rec Strassen (aZCode,aMaskBits) (bZCode,bMaskBits) (cZCode,cMaskBits)=

let GetSubcells (zCode, maskBits) =

let subMaskBits = maskBits - 2

let zI =

[|

(zCode , subMaskBits)

(zCode ||| (1<<<subMaskBits), subMaskBits)

(zCode ||| (2<<<subMaskBits), subMaskBits)

(zCode ||| (3<<<subMaskBits), subMaskBits)

|]

zI

if aMaskBits >= 3 then

let c1sub = GetSubcells (aZCode,aMaskBits)

let c2sub = GetSubcells (bZCode,aMaskBits)

let cRsub = GetSubcells (cZCode,aMaskBits)

Strassen c1sub.[0] c2sub.[0] cRsub.[0]

Strassen c1sub.[1] c2sub.[2] cRsub.[0]

Strassen c1sub.[0] c2sub.[1] cRsub.[1]

Strassen c1sub.[1] c2sub.[3] cRsub.[1]

Strassen c1sub.[2] c2sub.[0] cRsub.[2]

Strassen c1sub.[3] c2sub.[2] cRsub.[2]

Strassen c1sub.[2] c2sub.[1] cRsub.[3]

Strassen c1sub.[3] c2sub.[3] cRsub.[3]

else

let a0code=aZCode

let a1code=aZCode ||| 1

let a2code=aZCode ||| 2

let a3code=aZCode ||| 3

let b0code=bZCode

let b1code=bZCode ||| 1

let b2code=bZCode ||| 2

let b3code=bZCode ||| 3

let c0code=cZCode

let c1code=cZCode ||| 1

let c2code=cZCode ||| 2

let c3code=cZCode ||| 3

let p1=(zA.[a0code]+zA.[a3code])*(zB.[b0code]+ zB.[b3code]);

let p2=(zA.[a2code]+zA.[a3code])* zB.[b0code];

let p3=zA.[a0code] *(zB.[b1code]- zB.[b3code]);

let p4=zA.[a3code] *(zB.[b2code]- zB.[b0code]);

let p5=(zA.[a0code]+zA.[a1code])* zB.[b3code];

let p6=(zA.[a2code]-zA.[a0code])*(zB.[b0code]+ zB.[b1code]);

let p7=(zA.[a1code]-zA.[a3code])*(zB.[b2code]+ zB.[b3code]);

zC.[c0code] <- p1+p4-p5+p7+zC.[c0code];

zC.[c1code] <- p3+p5+zC.[c1code];

zC.[c2code] <- p2+p4+zC.[c2code];

zC.[c3code] <- p1-p2+p3+p6+zC.[c3code];

Strassen (ZCode,MaskBits)(ZCode,MaskBits)(ZCode,MaskBits)

zC

///Параллельная версия умножения с использованием метода штрассена

let ParStrassenMultiply (zA:float[]) (zB:float[]) (zC:float[]) =

let n=Array.length zA

let ZCode = 0

let MaskBits = int(Math.Log(float(n),float(2)))

let rec Strassen (aZCode,aMaskBits) (bZCode,bMaskBits) (cZCode,cMaskBits)=

let GetSubcells (zCode, maskBits) =

let subMaskBits = maskBits - 2

let zI =

[|

(zCode , subMaskBits)

(zCode ||| (1<<<subMaskBits), subMaskBits)

(zCode ||| (2<<<subMaskBits), subMaskBits)

(zCode ||| (3<<<subMaskBits), subMaskBits)

|]

zI

if aMaskBits >= 3 then

let c1sub = GetSubcells (aZCode,aMaskBits)

let c2sub = GetSubcells (bZCode,aMaskBits)

let cRsub = GetSubcells (cZCode,aMaskBits)

Strassen c1sub.[0] c2sub.[0] cRsub.[0]

Strassen c1sub.[1] c2sub.[2] cRsub.[0]

Strassen c1sub.[0] c2sub.[1] cRsub.[1]

Strassen c1sub.[1] c2sub.[3] cRsub.[1]

Strassen c1sub.[2] c2sub.[0] cRsub.[2]

Strassen c1sub.[3] c2sub.[2] cRsub.[2]

Strassen c1sub.[2] c2sub.[1] cRsub.[3]

Strassen c1sub.[3] c2sub.[3] cRsub.[3]

else

let a0code=aZCode

let a1code=aZCode ||| 1

let a2code=aZCode ||| 2

let a3code=aZCode ||| 3

let b0code=bZCode

let b1code=bZCode ||| 1

let b2code=bZCode ||| 2

let b3code=bZCode ||| 3

let c0code=cZCode

let c1code=cZCode ||| 1

let c2code=cZCode ||| 2

let c3code=cZCode ||| 3

let p1=(zA.[a0code]+zA.[a3code])*(zB.[b0code]+ zB.[b3code]);

let p2=(zA.[a2code]+zA.[a3code])* zB.[b0code];

let p3=zA.[a0code] *(zB.[b1code]- zB.[b3code]);

let p4=zA.[a3code] *(zB.[b2code]- zB.[b0code]);

let p5=(zA.[a0code]+zA.[a1code])* zB.[b3code];

let p6=(zA.[a2code]-zA.[a0code])*(zB.[b0code]+ zB.[b1code]);

let p7=(zA.[a1code]-zA.[a3code])*(zB.[b2code]+ zB.[b3code]);

zC.[c0code] <- p1+p4-p5+p7+zC.[c0code];

zC.[c1code] <- p3+p5+zC.[c1code];

zC.[c2code] <- p2+p4+zC.[c2code];

zC.[c3code] <- p1-p2+p3+p6+zC.[c3code];

let rec ParStrassen (aZCode,aMaskBits) (bZCode,bMaskBits) (cZCode,cMaskBits)=

let GetSubcells (zCode, maskBits) =

let subMaskBits = maskBits - 2

let zI =

[|

(zCode , subMaskBits)

(zCode ||| (1<<<subMaskBits), subMaskBits)

(zCode ||| (2<<<subMaskBits), subMaskBits)

(zCode ||| (3<<<subMaskBits), subMaskBits)

|]

zI

if aMaskBits >= 3 then

let c1sub = GetSubcells (aZCode,aMaskBits)

let c2sub = GetSubcells (bZCode,aMaskBits)

let cRsub = GetSubcells (cZCode,aMaskBits)

let tasks = [

async

{

Strassen c1sub.[0] c2sub.[0] cRsub.[0]

Strassen c1sub.[1] c2sub.[2] cRsub.[0]

}

async

{

Strassen c1sub.[0] c2sub.[1] cRsub.[1]

Strassen c1sub.[1] c2sub.[3] cRsub.[1]

}

async

{

Strassen c1sub.[2] c2sub.[0] cRsub.[2]

Strassen c1sub.[3] c2sub.[2] cRsub.[2]

}

async

{

Strassen c1sub.[2] c2sub.[1] cRsub.[3]

Strassen c1sub.[3] c2sub.[3] cRsub.[3]

}

]

tasks |> Async.Parallel |> Async.RunSynchronously |>ignore

else

let a0code=aZCode

let a1code=aZCode ||| 1

let a2code=aZCode ||| 2

let a3code=aZCode ||| 3

let b0code=bZCode

let b1code=bZCode ||| 1

let b2code=bZCode ||| 2

let b3code=bZCode ||| 3

let c0code=cZCode

let c1code=cZCode ||| 1

let c2code=cZCode ||| 2

let c3code=cZCode ||| 3

let p1=(zA.[a0code]+zA.[a3code])*(zB.[b0code]+ zB.[b3code]);

let p2=(zA.[a2code]+zA.[a3code])* zB.[b0code];

let p3=zA.[a0code] *(zB.[b1code]- zB.[b3code]);

let p4=zA.[a3code] *(zB.[b2code]- zB.[b0code]);

let p5=(zA.[a0code]+zA.[a1code])* zB.[b3code];

let p6=(zA.[a2code]-zA.[a0code])*(zB.[b0code]+ zB.[b1code]);

let p7=(zA.[a1code]-zA.[a3code])*(zB.[b2code]+ zB.[b3code]);

zC.[c0code] <- p1+p4-p5+p7+zC.[c0code];

zC.[c1code] <- p3+p5+zC.[c1code];

zC.[c2code] <- p2+p4+zC.[c2code];

zC.[c3code] <- p1-p2+p3+p6+zC.[c3code];

ParStrassen (ZCode,MaskBits)(ZCode,MaskBits)(ZCode,MaskBits)

zC

let n = 512 //1024,2048,4096

let mutable rand= new Random()

let rand1 = ref rand

let Generate i j (rand: Random ref) =

rand.Value <- new Random(i+j)

let x = rand.Value.NextDouble()

x

let A = Array2D.init n n (fun i j -> Generate i j rand1)

let B = Array2D.init n n (fun i j -> Generate i j rand1)

let C = Array2D.create n n 0.0

printf "[Lib multiply of %dx%d matrix] " n n

let x =

let A1= Math.Matrix.of_array2 A

let B1= Math.Matrix.of_array2 B

Time (fun () -> MultiplyX A1 B1)|>ignore

System.GC.Collect()

let (Za,Zb,Zc) = CreateZMatrix A B C

let Zc1= Array.copy Zc

let Zc2= Array.copy Zc

let Zc3= Array.copy Zc

printf "[Ordinary multiply of %dx%d matrix] " n n

Time (fun () -> Multiply A B C) |> ignore

printf "[Fast multiply of %dx%d matrix] " n n

Time (fun () -> PFXMultiply A B C)|>ignore

printf "[Z multiply of %dx%d matrix] " n n

let x0 =Math.Vector.of_array (Time(fun () ->ZMatrixMultiply Za Zb Zc))

printf "[Parallel Z multiply of %dx%d matrix] " n n

let x1 =Math.Vector.of_array (Time(fun () ->PZMatrixMultiply Za Zb Zc1))

printf "[Strassen multiply of %dx%d matrix] " n n

let x2 =Math.Vector.of_array (Time(fun () ->StrassenMultiply Za Zb Zc2))

printf "[Parallel Strassen multiply of %dx%d matrix] " n n

let x3 =Math.Vector.of_array (Time(fun () ->ParStrassenMultiply Za Zb Zc3))

let error = (x0-x2)+(x1-x3)

printf "error = "

Console.Write(Array.sum (Math.Vector.to_array error))

do

let PrintWithHeading s =

let line = String.replicate(s |> String.length) "="

printfn "%s\n%s" s line

printfn "RFE 2010\nVladimir Shchur\n"

PrintWithHeading "Matrix Multiply"

MatrixMultiplyDemo ()

printfn "\n\nPress any key to finish"

ПРИЛОЖЕНИЕ 2

Программа по обработке графического изображения множества Мандельброта

Program.fs - файл запуска Windows-приложения

module Program =

open System

open System.Windows.Forms

open MandelbrotFormModule

#if COMPILED

[<STAThread>]

do Application.EnableVisualStyles()

do Application.SetCompatibleTextRenderingDefault(false)

do Application.Run(new MandelbrotForm())

#endif

MandelbrotForm.fs -в этом файле производятся обработка пользовательских событий и вывод изображения на экран

module MandelbrotFormModule

open System

open System.Windows.Forms

open System.Drawing

open System.Drawing.Imaging

open System.Diagnostics

open System.Runtime.InteropServices

open System.Threading

open System.Threading.Tasks

open System.Collections.Concurrent

open System.IO

open MandelbrotGeneratorModule

/// <summary>Describes the bounds of the fractal to render.</summary>

let mutable _mandelbrotWindow = MandelbrotPosition( 2.9, 2.27, -0.75, 0.006)

/// <summary>The last known size of the main window.</summary>

let mutable _lastWindowSize = Size.Empty

/// <summary>The last known position of the mouse.</summary>

let mutable _lastMousePosition = Point(0,0)

/// <summary>The most recent cancellation source to cancel the last task.</summary>

let mutable _lastCancellation = new CancellationTokenSource()

/// <summary>The last time the image was updated.</summary>

let mutable _lastUpdateTime = DateTime.MinValue

/// <summary>Whether the left mouse button is currently pressed on the picture box.</summary>

let mutable _leftMouseDown = false

/// <summary>

/// The format string to use for the main form's title; {0} should be set to the number of

/// pixels per second rendered.

/// </summary>

let _formTitle = "Interactive Fractal F# ({0}x) - PPMS: {1} - Time: {2}";

/// <summary>Whether to use parallel rendering.</summary>

let mutable _parallelRendering = false;

type MandelbrotForm() as this =

inherit Form(ClientSize = new System.Drawing.Size(521, 452),

AutoScaleDimensions = new System.Drawing.SizeF(8.0f, 16.0f),

AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font,

Margin = new System.Windows.Forms.Padding(4, 4, 4, 4),

Name = "MainForm",

Text = "Mandelbrot in F#")

let pictureBox = new PictureBox(BackColor = System.Drawing.Color.Black,

Dock = System.Windows.Forms.DockStyle.Fill,

Location = Point(0, 0),

Margin = Padding(4, 4, 4, 4),

Name = "mandelbrotPb",

Size = Size(410, 369),

SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage,

TabIndex = 0,

TabStop = false)

let statusStrip = new StatusStrip( Location = Point(0, 427),

Name = "statusStrip",

Padding = Padding(1, 0, 19, 0),

Size = Size(521, 25),

TabIndex = 2,

Text = "statusStrip")

let toolStripStatusLabel = new ToolStripStatusLabel(Name = "toolStripStatusLabel1",

Size = Size(386, 20),

Text = "P: parallel mode, S: sequential mode, Double-click: zoom")

let countProc flag =

match flag with

| false -> 1

| true -> Environment.ProcessorCount

let UpdateImageAsync () =

// If there's currently an active task, cancel it! We don't care about it anymore.

if not (_lastCancellation.Equals(null))

then _lastCancellation.Cancel()

// Get the current size of the picture box

let renderSize = pictureBox.Size

// Keep track of the time this request was made. If multiple requests are executing,

// we want to only render the most recent one available rather than overwriting a more

// recent image with an older one.

let timeOfRequest = DateTime.UtcNow;

// Start a task to asynchronously render the fractal, and store the task

// so we can cancel it later as necessary

_lastCancellation <- new CancellationTokenSource()

let token = _lastCancellation.Token

// Start a task to asynchronously render the fractal

let task =

async{

// For diagnostic reasons, time how long the rendering takes

let sw = Stopwatch.StartNew()

// Render the fractal

let bmp = Create _mandelbrotWindow renderSize.Width renderSize.Height token _parallelRendering

if not (bmp.Equals(null))

then sw.Stop()

let ppms = (Convert.ToDouble(renderSize.Width * renderSize.Height)) / (double) sw.ElapsedMilliseconds;

// Update the fractal image asynchronously on the UI

// If this image is the most recent, store it into the picture box

// making sure to free the resources for the one currently in use.

// And update the form's title to reflect the rendering time.

if (timeOfRequest > _lastUpdateTime)

then //pictureBox.Image.Dispose()

pictureBox.Image <- bmp

//_lastUpdateTime <- timeOfRequest

this.Text <- String.Format(_formTitle,

countProc _parallelRendering,

ppms.ToString("F2"), sw.ElapsedMilliseconds.ToString())

let file = new FileStream("D:/Result/F#.txt", FileMode.Append)

let strw = new StreamWriter(file)

strw.WriteLine(this.Text)

strw.Flush()

strw.Close()

// If the image isn't the most recent, just get rid of it

else bmp.Dispose()

}

Async.StartAsTask(task,cancellationToken = token) |> ignore

// Add Handlers

do this.SuspendLayout()

do this.Controls.Add(pictureBox)

do statusStrip.Items.Add(toolStripStatusLabel)|>ignore

do this.Controls.Add(statusStrip)

do pictureBox.VisibleChanged.AddHandler(new EventHandler(fun s e -> this.pictureBox_VisibleChanged (s,e)))

do pictureBox.MouseDoubleClick.AddHandler(new MouseEventHandler(fun s e -> this.pictureBox_DoubleClick(s,e)))

do pictureBox.MouseDown.AddHandler(new MouseEventHandler(fun s e -> this.pictureBox_MouseDown(s,e)))

do pictureBox.MouseUp.AddHandler(new MouseEventHandler(fun s e -> this.pictureBox_MouseUp(s,e)))

do pictureBox.MouseMove.AddHandler(new MouseEventHandler(fun s e -> this.pictureBox_MouseMove(s,e)))

do pictureBox.Resize.AddHandler(new EventHandler(fun s e -> this.pictureBox_Resize(s,e)))

do this.ResumeLayout()

member this.pictureBox_VisibleChanged (s, e) =

if (pictureBox.Visible)

then _lastWindowSize <- this.Size

UpdateImageAsync |> ignore

member this.pictureBox_DoubleClick (s, e) =

// Center the image on the selected location

_mandelbrotWindow.CenterX <- _mandelbrotWindow.CenterX + (float (e.X - (pictureBox.Width / 2)) / (float pictureBox.Width)) * _mandelbrotWindow.Width

_mandelbrotWindow.CenterY <- _mandelbrotWindow.CenterY + (float (e.Y - (pictureBox.Height / 2)) / (float pictureBox.Height)) * _mandelbrotWindow.Height

// If the left mouse button was used, zoom in by a factor of 2; if the right mouse button, zoom

// out by a factor of 2

let factor =

if (e.Button = MouseButtons.Left)

then 0.5

else 2.0;

_mandelbrotWindow.Width <- _mandelbrotWindow.Width*factor

_mandelbrotWindow.Height <- _mandelbrotWindow.Height*factor

// Update the image

UpdateImageAsync()

member this.pictureBox_MouseDown (s, e) =

// Record that mouse button is being pressed

if (e.Button = MouseButtons.Left)

then _lastMousePosition <- e.Location

_leftMouseDown <- true

member this.pictureBox_MouseUp (s, e) =

// Record that the mouse button is being released

if (e.Button = MouseButtons.Left)

then _lastMousePosition <- e.Location

_leftMouseDown <- false

member this.pictureBox_MouseMove (s, e) =

// Determine how far the mouse has moved. If it moved at all...

let delta = new Point(e.X - _lastMousePosition.X, e.Y - _lastMousePosition.Y)

if not (delta = Point.Empty)

then // And if the left mouse button is down...

if (_leftMouseDown)

then

// Determine how much the mouse moved in fractal coordinates

let fractalMoveX = (float delta.X) * _mandelbrotWindow.Width / (float pictureBox.Width)

let fractalMoveY = (float delta.Y) * _mandelbrotWindow.Height / (float pictureBox.Height)

// Shift the fractal window accordingly

_mandelbrotWindow.CenterX <- _mandelbrotWindow.CenterX - fractalMoveX

_mandelbrotWindow.CenterY <- _mandelbrotWindow.CenterY - fractalMoveY

// And update the image

UpdateImageAsync()

// Record the new mouse position

_lastMousePosition <- e.Location

member this.pictureBox_Resize (s, e) =

// If the window has been resized

if not (this.Size = _lastWindowSize)

then

// Scale the mandelbrot image by the same factor so that its visual size doesn't change

if not (_lastWindowSize.Width = 0)

then

let xFactor = (float this.Size.Width) / (float _lastWindowSize.Width)

_mandelbrotWindow.Width <- _mandelbrotWindow.Width * xFactor

if not (_lastWindowSize.Height = 0)

then

let yFactor = (float this.Size.Height) / (float _lastWindowSize.Height)

_mandelbrotWindow.Height <- _mandelbrotWindow.Height * yFactor

// Record the new window size

_lastWindowSize <- this.Size

// Update the image

UpdateImageAsync()

override this.OnKeyDown e =

// Handle the key pressed

base.OnKeyDown e

match e.KeyCode with

Keys.R ->

_mandelbrotWindow <- new MandelbrotPosition( 2.9, 2.27, -0.75, 0.006)

use tempForm = new MandelbrotForm()

let xFactor = (float this.Size.Width) / (float tempForm.Width)

let yFactor = (float this.Size.Height) / (float tempForm.Height)

_mandelbrotWindow.Width <- _mandelbrotWindow.Width * xFactor

_mandelbrotWindow.Height <- _mandelbrotWindow.Height * yFactor

UpdateImageAsync()

|Keys.S ->

_parallelRendering <- false

UpdateImageAsync()

|Keys.P ->

_parallelRendering <- true

UpdateImageAsync()

|_ -> ()

MandelbrotGenerator.fs -в этом файле производятся расчеты цвета пикселей для построения множества Мандельброта

module MandelbrotGeneratorModule

#nowarn "9"

open System

open System.IO

open System.Windows.Forms

open System.Drawing

open System.Drawing.Imaging

open System.Diagnostics

open System.Runtime.InteropServices

open System.Threading

open System.Threading.Tasks

open System.Numerics

open Microsoft.FSharp.NativeInterop

open Microsoft.FSharp.Collections

open System.Collections.Concurrent

/// <summary>Represents the bounds and location of the mandelbrot fractal to be rendered.</summary>

type public MandelbrotPosition =

struct

val mutable public Width: float

val mutable public Height: float

val mutable public CenterX: float

val mutable public CenterY: float

new(width: float, height: float, centerX: float, centerY: float) =

{ Width = width ; Height = height; CenterX = centerX; CenterY = centerY }

end

/// <summary>Create the color palette to be used for all fractals.</summary>

/// <returns>A 256-color array that can be stored into an 8bpp Bitmap's ColorPalette.</returns>

let CreatePaletteColors : Color[] =

let paletteColors : Color[] = Array.init 256 (fun i -> Color.FromArgb(0, i * 5 % 256, i * 5 % 256))

paletteColors

/// <summary>The 256 color palette to use for all fractals.</summary>

let _paletteColors = CreatePaletteColors

/// <summary>Copy our precreated color palette into the target Bitmap.</summary>


Подобные документы

  • Приобретение теоретических и практических навыков программирования на языке Паскаль. Математическая формулировка задачи и выбор метода обработки информации. Разработка алгоритма и его описание. Описание программы. Форма представления исходных данных.

    курсовая работа [224,3 K], добавлен 11.02.2016

  • Создание информационной системы обработки матриц. Общая характеристика программного обеспечения, которое реализует выполнение заданных функций. Программа разработана с использованием среды визуального программирования Delphi 7 и языка Object Pascal.

    курсовая работа [373,4 K], добавлен 14.01.2011

  • Изучение организации диалоговой программы и закрепления основных элементов программирования на языке Паскаль и Си (Delphi, C++ Builder). Описание представления информации в программах на языках высокого уровня. Сравнительная характеристика Delphi и C++.

    курсовая работа [3,1 M], добавлен 27.02.2015

  • Задачи цифровой обработки изображений. Методы пороговой сегментации. Создание программы представления рисунка в виде матрицы и применения к нему пороговой обработки. Разработка интерфейса программы загрузки и фильтрации изображения с выбранным порогом.

    курсовая работа [2,0 M], добавлен 12.11.2012

  • Обзор существующих моделей параллельного программирования, основные средства отладки эффективности MPI-программ, общие проблемы всех средств трассировки. Создание экспериментальной системы отладки эффективности MPI-программ, этапы работы анализатора.

    дипломная работа [767,2 K], добавлен 14.10.2010

  • Анализ затрат и прибыли. Создание программного проекта для решения задачи о прибыли и убытках на языке программирования C#. Использование функций и переменных, компиляция программы. Алгоритмы и структуры данных. Тестирование программного обеспечения.

    курсовая работа [1,2 M], добавлен 03.01.2015

  • Понятия структурного программирования и алгоритма решения задачи. Краткая история развития языков программирования от машинных до языков ассемблера и языков высокого уровня. Процедурное программирование на C#. Методы и программы для моделирования.

    учебное пособие [1,7 M], добавлен 26.10.2010

  • Особенности способов описания языков программирования. Язык программирования как способ записи программ на ЭВМ в понятной для компьютера форме. Характеристика языка Паскаль, анализ стандартных его функций. Анализ примеров записи арифметических выражений.

    курсовая работа [292,0 K], добавлен 18.03.2013

  • Характеристика языков программирования: краткая история, хронология. Основные виды языков программирования: ассемблер; бейсик. Создание и использование формул в Excel. Применение операторов в формулах. Использование функций в Excel. Сайт дома отдыха.

    отчет по практике [139,1 K], добавлен 03.06.2011

  • Обзор основных используемых языков программирования (С++, Java, Pascal). Анализ существующих методов шифрования паролей. Основные понятия объектно-ориентированного программирования. Реализация приложения для генерирования паролей на языке Object Pascal.

    курсовая работа [822,4 K], добавлен 07.07.2012

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.