Параллельное программирование - C#
Формулировка задачи:
Решил накидать простенькую задачу: выполнить какие-нибудь операции над большим блоком данных. И это все распараллелить. Это можно сделать 2мя способами:
1. Параллелить вызов коротеньких процедур. Но тогда будет очень много времени тратиться на то, чтобы передать управление другому ядру. Ведь так? ну нет смысла параллелить операцию 2+2.
2. Параллелить обработку блоков данных. Логично предположить. что когда параллельно запускаться будут тяжелые процедуруы, эффект от параллельности будет гораздо выше.
Однако оказались весьма странные результаты. Почему-то первоначальный эффект от параллельности отрицательный.
Вот код простой программки, которая наглядно всё демонстрирует:
А вот результат.
1 итерация:
1 поток, время: 2,5947303
2 поток, время: 1,5046816
3 поток, время: 1,2435103
4 поток, время: 1,1743574
5 поток, время: 1,8177255
6 поток, время: 1,8564871
7 поток, время: 1,7038264
8 поток, время: 1,7404472
Блок итераций:
1 поток, время: 1,2824387
2 поток, время: 1,2592897
3 поток, время: 1,3303499
4 поток, время: 1,3710368
5 поток, время: 1,4195757
6 поток, время: 1,4460356
7 поток, время: 1,5213963
8 поток, время: 1,6072681
Как видно, во втором случае результат очень плохой. То есть распараллеливание медленно отрабатывает. Почему так? Ведь по логике второй способ распараллеливания должен быть лучше первого?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
double [] _dArray = new double[10000000];
double[] _dArray2 = new double[10000000];
int _iThreads = 8;
int _iSizeBlock;
private void button1_Click(object sender, EventArgs e)
{
_iSizeBlock = _dArray.Length / _iThreads;//размер блока
//заполним массив случайно
Random r = new Random();
for (int i = 0; i < _dArray.Length; i++)
{
_dArray[i] = r.NextDouble();
_dArray2[i] = _dArray[i];
}
richTextBox1.Text = "1 итерация:\r\n";
for (int i = 1; i <= 8; i++)
{
ParallelOptions options = new ParallelOptions
{
MaxDegreeOfParallelism = i
};
Stopwatch st1 = new Stopwatch();
st1.Start();
Parallel.For(0, _dArray.Length, options, parallelOne);
st1.Stop();
richTextBox1.Text += i.ToString() + " поток, время: " + st1.Elapsed.TotalSeconds.ToString() + "\r\n";
}
richTextBox1.Text += "Блок итераций:\r\n";
for (int i = 1; i <= 8; i++)
{
ParallelOptions options = new ParallelOptions
{
MaxDegreeOfParallelism = i
};
Stopwatch st1 = new Stopwatch();
st1.Start();
Parallel.For(0, i, options, ParallelBlock);
st1.Stop();
richTextBox1.Text += i.ToString() + " поток, время: " + st1.Elapsed.TotalSeconds.ToString() + "\r\n";
}
}
private void ParallelBlock(int iIndex)
{
int iStart = iIndex * _iSizeBlock;
int iEnd = iStart + _iSizeBlock;
//iIndex - номер блока
for (int i = iStart; i < iEnd; i++)
{
_dArray[i] = Someoperations(_dArray[i]);
}
}
private void parallelOne(int iIndex)
{
_dArray[iIndex] = Someoperations(_dArray[iIndex]);
}
private double Someoperations(double dInput)
{
double Result = Math.Sin(dInput) * Math.Log(dInput + 10);
Result = Math.Pow(Result, 10);
Result += Math.Abs(Math.Cos(Result));
Result += Math.Sqrt(Result);
Result = Math.Pow(Result, 2);
return Result;
}
}
}Решение задачи: «Параллельное программирование»
textual
Листинг программы
public OneObject(Image<Lab, byte> imgSource,//исходное изображение
Image<Bgr, byte> imgBinarized,//бинаризованное изображение
Rectangle rect//область объекта
)
{
watchSegmentation = new Stopwatch();
int iStopY = rect.Y + rect.Height;
int iStopX = rect.X + rect.Width;
_iObjectpixels = 0;
//узнать число пикселей объекта (по бинаризованному изображению)
for (int y = rect.Y; y < iStopY; y++)
{
for (int x = rect.X; x < iStopX; x++)
{
if ((imgBinarized.Data[y, x, 0] < 123) && (imgBinarized.Data[y, x, 1] < 123) && (imgBinarized.Data[y, x, 2] < 123))
{
_iObjectpixels++;
}
}
}
//поместить в матрицу пиксели объекта
matrSourceObject = new Matrix<float>(_iObjectpixels, 1, 3);
matrClusteredObject = new Matrix<int>(_iObjectpixels, 1);
int iObjectpixels = 0;
//заполнить матрицу изображениями объекта без фона
for (int y = rect.Y; y < iStopY; y++)
{
for (int x = rect.X; x < iStopX; x++)
{
if ((imgBinarized.Data[y, x, 0] < 123) && (imgBinarized.Data[y, x, 1] < 123) && (imgBinarized.Data[y, x, 2] < 123))
{
matrSourceObject.Data[iObjectpixels, 0] = (float)imgSource[y, x].X;
matrSourceObject.Data[iObjectpixels, 1] = (float)imgSource[y, x].Y;
matrSourceObject.Data[iObjectpixels, 2] = (float)imgSource[y, x].Z;
iObjectpixels++;
}
}
}
MCvTermCriteria term = new MCvTermCriteria(50, 0.9);
term.Type = TermCritType.Eps | TermCritType.Iter;
clusterCount = 2;
int attempts = 5;
Matrix<Single> centers = new Matrix<Single>(clusterCount, imgSource.Rows * imgSource.Cols);
//сегментация
//еще нужно будет в цикле определять число кластеров
//но это потом
double divLastStep = 0, divThisStep = 0, dLastSterpEntr = 0;
double dEntropy = 0;
bool bEntrStop = false;
bool bSKOStop = false;
List<double> listDobleEntropy = new List<double>();
List<double> listDoubleSKOMult = new List<double>();
watchSegmentation.Restart();
while (true)
{
listMatr.Clear();
try
{
watchSegmentation.Start();
CvInvoke.Kmeans(matrSourceObject, clusterCount, matrClusteredObject, term, attempts, KMeansInitType.PPCenters, centers);
watchSegmentation.Stop();
}
catch
{
clusterCount--;
listMatr.Clear();
CvInvoke.Kmeans(matrSourceObject, clusterCount, matrClusteredObject, term, attempts, KMeansInitType.PPCenters, centers);
//поместить все точки объекта в текущею ячейку, в которой будет список из сегментов, в каждом из которых содержатся точки
MyClass.pointsIntoListClusters(matrSourceObject, matrClusteredObject, listMatr, clusterCount);
break;
}
//поместить все точки объекта в текущею ячейку, в которой будет список из сегментов, в каждом из которых содержатся точки
MyClass.pointsIntoListClusters(matrSourceObject, matrClusteredObject, listMatr, clusterCount);
//определяем максимальную энтропию по всем кластерам
listDobleEntropy.Clear();
listDoubleSKOMult.Clear();
//watchSegmentation.Start();
listDobleEntropy = MyMath.GetEntropyInAllClusters(listMatr);
//watchSegmentation.Start();
dEntropy = MyMath.Max(listDobleEntropy);
//проверка критерия останова по энтропии
if (dEntropy < dLastSterpEntr)
{
bEntrStop = true;
}
//определяем произведение СКО компонент для каждого кластера
double[] dSKOMult = new double[clusterCount];
for (int i = 0; i < clusterCount; i++)
{
dSKOMult[i] = 1;
dSKOMult[i] *= MyMath.GetMeanSquareDiviation(listMatr[i], 0);
dSKOMult[i] *= MyMath.GetMeanSquareDiviation(listMatr[i], 1);
dSKOMult[i] *= MyMath.GetMeanSquareDiviation(listMatr[i], 2);
}
divThisStep = MyMath.Max(dSKOMult) / MyMath.Min(dSKOMult);
if (divThisStep < divLastStep)
{
bSKOStop = true;
}
//условие выхода из цикла
if ((bSKOStop && bEntrStop) || (clusterCount >= 9))
{
clusterCount++;
watchSegmentation.Start();
try
{
listMatr.Clear();
CvInvoke.Kmeans(matrSourceObject, clusterCount, matrClusteredObject, term, attempts, KMeansInitType.PPCenters, centers);
}
catch
{
listMatr.Clear();
clusterCount--;
CvInvoke.Kmeans(matrSourceObject, clusterCount, matrClusteredObject, term, attempts, KMeansInitType.PPCenters, centers);
//поместить все точки объекта в текущею ячейку, в которой будет список из сегментов, в каждом из которых содержатся точки
}
finally
{
MyClass.pointsIntoListClusters(matrSourceObject, matrClusteredObject, listMatr, clusterCount);
}
watchSegmentation.Stop();
break;
}
divLastStep = divThisStep;
dLastSterpEntr = MyMath.Max(listDobleEntropy);
clusterCount++;
}
iSegmentsInObject = clusterCount;
//вывод результата в красивой картинке
imgSegmentedObject = new Image<Bgr, byte>(new System.Drawing.Size(rect.Width, rect.Height));
imgObject = new Image<Lab, byte>(new System.Drawing.Size(rect.Width, rect.Height));
iObjectpixels = 0;
int iColorNumber = 0;
int iy = 0;
for (int y = rect.Y; y < iStopY; y++)
{
int ix = 0;
for (int x = rect.X; x < iStopX; x++)
{
if ((imgBinarized.Data[y, x, 0] < 123) && (imgBinarized.Data[y, x, 1] < 123) && (imgBinarized.Data[y, x, 2] < 123))
{
iColorNumber = matrClusteredObject.Data[iObjectpixels, 0];
imgSegmentedObject.Data[iy, ix, 0] = (byte)clusterColors[iColorNumber].Red;
imgSegmentedObject.Data[iy, ix, 1] = (byte)clusterColors[iColorNumber].Green;
imgSegmentedObject.Data[iy, ix, 2] = (byte)clusterColors[iColorNumber].Blue;
imgObject.Data[iy, ix, 0] = (byte)matrSourceObject.Data[iObjectpixels, 0];
imgObject.Data[iy, ix, 1] = (byte)matrSourceObject.Data[iObjectpixels, 1];
imgObject.Data[iy, ix, 2] = (byte)matrSourceObject.Data[iObjectpixels, 2];
iObjectpixels++;
}
else
{
imgSegmentedObject.Data[iy, ix, 0] = 255;
imgSegmentedObject.Data[iy, ix, 1] = 255;
imgSegmentedObject.Data[iy, ix, 2] = 255;
imgObject.Data[iy, ix, 0] = 255;
imgObject.Data[iy, ix, 1] = 125;
imgObject.Data[iy, ix, 2] = 125;
}
ix++;
}
iy++;
}
}