Mátrix szorzás
Feladat: Mátrixszorzás megvalósítása párhuzamos programmal.
Szerző: Fóthi Áron
Készítés éve: 2011
Használt fejlesztőkörnyezet:
Az algoritmus implementálásához, és a tesztmátrix létrehozásához: R 2.12.2 és az Rdsm csomag
A grafikus futtató környezethez: VS 2010, statconnDCOM
A párhuzamos mátrix szorzást a matmul függvényben implementáltam:
matmul <- function(m1, m2, prd) {
myid <- myinfo$myid
k <- if(class(m1) == "big.matrix") dim(m1)[2] else m1$size[2]
chunksize <- floor(k/myinfo$nclnt)
firstcol <- 1 + (myid-1) * chunksize
if(myid == myinfo$nclnt) lastcol <- k
else lastcol <- firstcol + chunksize - 1
prd[,firstcol:lastcol] <- m1[,] %*% m2[,firstcol:lastcol]
barr()
}
Az algoritmus a második mátrix oszlopait felosztja szálak között, és ezeket a részmátrixokat szorozza össze az első mátrixal. A szorzatok eredménye a közös prd mátrixba kerül. A bar() függvény biztosítja, hogy a szálak egyszerre érjenek véget.
A test függvény a létrehozza és kitölti az összeszorzandó mátrixokat, és felhívja a matmul függvényt.
test <- function(size, pr=TRUE) {
long<-size * size
mx <- matrix(1:long,nrow=size)
newbm("ma","double",size,size,mx)
newbm("mb","double",size,size,mx)
newbm("mc","double",size,size,mx)
matmul(ma,mb,mc)
if (myinfo$myid == 1)
if(pr) print(mc[,])
else mc[,]
}
Az első kliens szál kiírja a mátrix tartalmát a konzolra.
A pr paraméterrel mondhatjuk meg, hogy végrehajsa-e az előző lépést, amire majd a grafikus futtatókörnyezetből való futtatásnál lesz szükség.
A program futtatása
-Nyiss az indítandó szálakkal megyegyező számú R Console-t, plussz egyet az Rdsm szerver indításához.
-Töltsd be az Rdsm és a bigmemory csomagokat
> library(Rdsm)
> library(bigmemory)
-Indítsd el a szervert (N az eliindítandó szálak száma)
> srvr(port=2000,ncon=N)
-Az összes kliens szálon töltsd be az Rdsm és a bigmemory csomagokat
> library(Rdsm)
> library(bigmemory)
-olvastasd be a scriptet
>source("MatMul.R")
-Inicializáld a szálakat
>init()
-Futtasd a test() függvényt (M a mátrix dimenziója)
>test(M)
Mivel az előző lépések végrehajtása elég körülményes, és a kézi indítások miatt több időt vesztünk, mint amennyit nyerhetünk a párhuzamos futtatással, érdemes írni egy futtató programot, amely elvégzi helyettünk ezeket a lépéseket.
Ehhez a legfontosabb eszköz a statconnDCOM. Lehetővé teszi R scriptek beágyazását .COM-os környezetbe. (Nem része az R-nek! Külön kell telepíteni)
A következő példa azt szemlélteti, hogy hogyan lehet ezt megvalósítan C# .NET-et választva, ráadásul párhuzamos megoldással.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
statconnDCOM
using STATCONNECTORCLNTLib;
using StatConnectorCommonLib;
using STATCONNECTORSRVLib;
namespace R_Test
{
class R
{
protected static List threads;
protected static Dictionary connectors;
protected static double[,] d;
public static int size;
static Form1 m_form;
Minden szálhoz hozzárendelünk egy connectort
public R(int szalakSzama, Form1 form)
{
m_form = form;
ewh = new EventWaitHandle(false, EventResetMode.ManualReset);
threads = new List();
connectors = new Dictionary();
threads.Add(new Thread(DoServerWork));
threads.First().IsBackground = true;
connectors.Add(0, new StatConnector());
threads.First().Start(szalakSzama);
Thread.Sleep(200);
for (int i = 1; i < szalakSzama + 1; i++)
{
Thread thread = new Thread(DoWork);
thread.IsBackground = true;
connectors.Add(i, new StatConnector());
Thread.Sleep(200);
thread.Start(i);
threads.Add(thread);
}
}
public static EventWaitHandle ewh;
Elindítjuk a szervert
protected static void DoServerWork(object szalakSzama)
{
try
{
connectors.First().Value.Init("R");
connectors.First().Value.EvaluateNoReturn("library(Rdsm)");
connectors.First().Value.EvaluateNoReturn("library(bigmemory)");
connectors.First().Value.EvaluateNoReturn(string.Format("srvr(port=2000,ncon={0})", szalakSzama));
}
catch (Exception ex)
{
}
}
Elindítjuk a klienseket.
Ha az első kliens végrehajtotta a függvényt, akkor a connector Evaluate függvényének segítségével vissza tudjuk kapni az R töl a szorzás eredményét.
protected static void DoWork(object index)
{
try
{
connectors[(int)index].Init("R");
connectors[(int)index].EvaluateNoReturn("library(Rdsm)");
connectors[(int)index].EvaluateNoReturn("library(bigmemory)");
string matmul = System.Environment.CurrentDirectory + "/MatMul.R";
string csere = matmul.Replace('\\', '/');
connectors[(int)index].EvaluateNoReturn(string.Format("source(\"{0}\")", csere));
connectors[(int)index].EvaluateNoReturn("init()");
if ((int)index == threads.Count-1)
m_form.Invoke(m_form.ThreadsStarted);
for (;;)
{
ewh.WaitOne();
if ((int)index == 1)
{
d = (double[,])connectors[(int)index].Evaluate(string.Format("test({0}, FALSE)", size));
m_form.Invoke(m_form.DelegateThreadFinished, new Object[] { d });
ewh.Reset();
}
else
{
connectors[(int)index].EvaluateNoReturn(string.Format("test({0}, FALSE)", size));
}
}
}
catch (Exception ex)
{
}
}
}
}
A teljes kód megtalálható a példaprogramok között.