shithub: riscv

Download patch

ref: 85b8d253d496c115766a37f51ea72cbec78090a8
parent: 4e8494aad79a124f2b15ede9e7873fed46e1bb0a
author: spew <devnull@localhost>
date: Sat Mar 25 09:05:47 EDT 2017

games/galaxy: parallelize gravitational force calculations

Once the Barnes-Hut tree is constructed, the gravitational
force calculations can be done in parallel by dividing the
bodies up between a number of procs.

--- a/sys/man/1/galaxy
+++ b/sys/man/1/galaxy
@@ -102,23 +102,11 @@
 Certain aspects of the galaxy simulator are controlled by
 the following options:
 .TP
-.BI -t " throttle"
-Causes the process that calculates forces to relinquish
-the processor for
-.I throttle
-milliseconds after each calculation.
-.TP
 .BI -G " gravity"
 Sets the gravitational constant to
 .I gravity.
 The default value is 1.
 .TP
-.BI -ε " softening"
-Sets the
-.I softening
-factor to prevent gravitational singularities during
-collisions or near-collisions. The default value is 500.
-.TP
 .BI -f " file"
 Reads the galaxy file
 .I file
@@ -127,6 +115,25 @@
 .TP
 .B -i
 Reads a galaxy file from standard input.
+.TP
+.BI -p " procs"
+Specifies the number of extra processes to use in order
+to calculate the gravitational force on each body in
+parallel.
+The default value is
+.BR $NPROC-1 .
+.TP
+.BI -t " throttle"
+Causes the process that calculates forces to relinquish
+the processor for
+.I throttle
+milliseconds after each calculation.
+.TP
+.BI -ε " softening"
+Sets the
+.I softening
+factor to prevent gravitational singularities during
+collisions or near-collisions. The default value is 500.
 .SS Mkgalaxy
 .I Mkgalaxy
 is a utility to create galaxies for simulation.
--- a/sys/src/games/galaxy/body.c
+++ b/sys/src/games/galaxy/body.c
@@ -11,7 +11,7 @@
 	glxy.a = calloc(5, sizeof(Body));
 	if(glxy.a == nil)
 		sysfatal("could not allocate glxy: %r");
-	glxy.l = 0;
+	glxy.nb = 0;
 	glxy.max = 5;
 }
 
@@ -20,13 +20,13 @@
 {
 	Body *b;
 
-	if(glxy.l == glxy.max) {
+	if(glxy.nb == glxy.max) {
 		glxy.max *= 2;
 		glxy.a = realloc(glxy.a, sizeof(Body) * glxy.max);
 		if(glxy.a == nil)
 			sysfatal("could not realloc glxy: %r");
 	}
-	b = glxy.a + glxy.l++;
+	b = glxy.a + glxy.nb++;
 	*b = ZB;
 	return b;
 }
@@ -64,11 +64,11 @@
 	Vector gc, gcv;
 	double mass;
 
-	if(glxy.l == 0)
+	if(glxy.nb == 0)
 		return (Vector){0, 0};
 
 	gc.x = gc.y = gcv.x = gcv.y = mass = 0;
-	for(b = glxy.a; b < glxy.a+glxy.l; b++) {
+	for(b = glxy.a; b < glxy.a+glxy.nb; b++) {
 		gc.x += b->x * b->mass;
 		gc.y += b->y * b->mass;
 		gcv.x += b->v.x * b->mass;
@@ -79,7 +79,7 @@
 	gc.y /= mass;
 	gcv.x /= mass;
 	gcv.y /= mass;
-	for(b = glxy.a; b < glxy.a+glxy.l; b++) {
+	for(b = glxy.a; b < glxy.a+glxy.nb; b++) {
 		b->x -= gc.x;
 		b->y -= gc.y;
 		b->v.x -= gcv.x;
@@ -145,7 +145,7 @@
 	int cmd, len;
 	Body *b;
 
-	glxy.l = 0;
+	glxy.nb = 0;
 	Binit(&bin, fd, OREAD);
 	for(;;) {
 		line = Brdline(&bin, ' ');
@@ -212,7 +212,7 @@
 	Bprint(&bout, "DT %g\n", dt);
 	Bprint(&bout, "GRAV %g\n", G);
 
-	for(b = glxy.a; b < glxy.a + glxy.l; b++)
+	for(b = glxy.a; b < glxy.a + glxy.nb; b++)
 		Bprint(&bout, "%B\n", b);
 
 	Bterm(&bout);
--- a/sys/src/games/galaxy/galaxy.c
+++ b/sys/src/games/galaxy/galaxy.c
@@ -46,7 +46,6 @@
 };
 
 enum {
-	STK = 8192,
 	DOBODY = 0,
 	SPEED,
 	GRAV,
@@ -68,7 +67,7 @@
 	LIM = 10,
 	dt²;
 char *file;
-int showv, showa, throttle, paused;
+int showv, showa, paused;
 
 char *menustr[] = {
 	[DOBODY]	"new body",
@@ -149,7 +148,7 @@
 	Point p;
 	static char buf[1024];
 
-	snprint(buf, sizeof(buf), "Number of bodies: %d", glxy.l);
+	snprint(buf, sizeof(buf), "Number of bodies: %d", glxy.nb);
 	p = addpt(screen->r.min, (Point){5, 3});
 	string(screen, p, display->white, ZP, font, buf);
 
@@ -170,7 +169,7 @@
 	int s;
 
 	draw(screen, screen->r, display->black, 0, ZP);
-	for(b = glxy.a; b < glxy.a + glxy.l; b++) {
+	for(b = glxy.a; b < glxy.a + glxy.nb; b++) {
 		pos = topoint(b->Vector);
 		s = b->size/scale;
 		fillellipse(screen, pos, s, s, b->col, ZP);
@@ -529,57 +528,6 @@
 	}
 }
 
-/* verlet barnes-hut */
-void
-simulate(void*)
-{
-	Body *b;
-	double f;
-
-	threadsetname("simulate");
-
-	for(;;) {
-		qlock(&glxy);
-
-		if(throttle)
-			sleep(throttle);
-
-		drawglxy();
-
-Again:
-		space.t = EMPTY;
-		quads.l = 0;
-		STATS(quaddepth = 0;)
-		for(b = glxy.a; b < glxy.a + glxy.l; b++) {
-			if(quadins(b, LIM) == -1) {
-				growquads();
-				goto Again;
-			}
-		}
-
-		STATS(avgcalcs = 0;)
-		for(b = glxy.a; b < glxy.a + glxy.l; b++) {
-			b->a.x = b->newa.x;
-			b->a.y = b->newa.y;
-			b->newa.x = b->newa.y = 0;
-			STATS(calcs = 0;)
-			quadcalc(b, space, LIM);
-			STATS(avgcalcs += calcs;)
-		}
-		STATS(avgcalcs /= glxy.l;)
-
-		for(b = glxy.a; b < glxy.a + glxy.l; b++) {
-			b->x += dt*b->v.x + dt²*b->a.x/2;
-			b->y += dt*b->v.y + dt²*b->a.y/2;
-			b->v.x += dt*(b->a.x + b->newa.x)/2;
-			b->v.y += dt*(b->a.y + b->newa.y)/2;
-			CHECKLIM(b, f);
-		}
-
-		qunlock(&glxy);
-	}
-}
-
 Vector
 tovector(Point p)
 {
@@ -600,6 +548,7 @@
 void
 threadmain(int argc, char **argv)
 {
+	char* nproc;
 	int doload;
 
 	doload = 0;
@@ -607,6 +556,9 @@
 	default:
 		usage();
 		break;
+	case 'p':
+		extraproc = strtol(EARGF(usage()), nil, 0);
+		break;
 	case 't':
 		throttle = strtol(EARGF(usage()), nil, 0);
 		break;
@@ -635,6 +587,16 @@
 		close(0);
 		if(open(file, OREAD) != 0)
 			sysfatal("threadmain: could not open file: %r");
+	}
+
+	if(extraproc < 0) {
+		nproc = getenv("NPROC");
+		if(nproc == nil)
+			extraproc = 0;
+		else
+			extraproc = strtol(nproc, nil, 10) - 1;
+		if(extraproc < 0)
+			extraproc = 0;
 	}
 
 	if(initdraw(nil, nil, "Galaxy") < 0)
--- a/sys/src/games/galaxy/galaxy.h
+++ b/sys/src/games/galaxy/galaxy.h
@@ -33,7 +33,7 @@
 struct {
 	QLock;
 	Body *a;
-	int l, max;
+	int nb, max;
 } glxy;
 
 struct {
@@ -49,11 +49,6 @@
 	BODY,
 };
 
-Point orig;
-double G, θ, scale, ε, LIM, dt, dt²;
-Body ZB;
-QB space;
-
 Image *randcol(void);
 Point topoint(Vector);
 Vector tovector(Point);
@@ -65,16 +60,24 @@
 int Bfmt(Fmt*);
 void readglxy(int);
 void writeglxy(int);
+void drawglxy(void);
 
+void simulate(void*);
+
 void quadcalc(Body*, QB, double);
 int quadins(Body*, double);
 void growquads(void);
 void quadsinit(void);
 
-int stats;
-int quaddepth;
-int calcs;
-double avgcalcs;
+int stats, quaddepth, calcs, extraproc, throttle;
+double G, θ, scale, ε, LIM, dt, dt², avgcalcs;
+Point orig;
+QB space;
+Body ZB;
+
+enum {
+	STK = 8192,
+};
 
 #define STATS(e) if(stats) {e}
 
--- a/sys/src/games/galaxy/mkfile
+++ b/sys/src/games/galaxy/mkfile
@@ -2,7 +2,7 @@
 
 TARG=galaxy mkgalaxy
 BIN=/$objtype/bin/games
-OGALAXY=galaxy.$O quad.$O body.$O
+OGALAXY=galaxy.$O quad.$O body.$O simulate.$O
 OMKGALAXY=mkgalaxy.$O body.$O
 
 </sys/src/cmd/mkmany
--- a/sys/src/games/galaxy/mkgalaxy.c
+++ b/sys/src/games/galaxy/mkgalaxy.c
@@ -152,7 +152,7 @@
 	if(argc != 1)
 		usage();
 
-	new = glxy.l;
+	new = glxy.nb;
 	lim = strtod(*argv, nil);
 	mkbodies(lim);
 
@@ -160,7 +160,7 @@
 	for(b = glxy.a; b < glxy.a + new; b++)
 		Bprint(&bout, "%B\n", b);
 
-	for(b = glxy.a+new; b < glxy.a+glxy.l; b++) {
+	for(b = glxy.a+new; b < glxy.a+glxy.nb; b++) {
 		b->x += o.x;
 		b->y += o.y;
 		Bprint(&bout, "%B\n", b);
--- /dev/null
+++ b/sys/src/games/galaxy/simulate.c
@@ -1,0 +1,142 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include "galaxy.h"
+
+int extraproc = -1, throttle;
+
+static QLock* golock;
+static Rendez* gorend;
+static int* go;
+
+static QLock runninglock;
+static Rendez runningrend;
+static int running;
+
+static void
+calcproc(void *v)
+{
+	Body *b, *e;
+	int nbody;
+	int pid;
+
+	pid = (uintptr)v;
+
+	for(;;) {
+		qlock(golock+pid);
+		while(!go[pid])
+			rsleep(gorend+pid);
+		go[pid] = 0;
+		qunlock(golock+pid);
+
+		nbody = glxy.nb / (extraproc+1);
+		b = glxy.a + nbody * pid;
+		e = b + nbody;
+		while(b < e) {
+			b->a.x = b->newa.x;
+			b->a.y = b->newa.y;
+			b->newa.x = b->newa.y = 0;
+			quadcalc(b++, space, LIM);
+		}	
+
+		qlock(&runninglock);
+		if(--running == 0)
+			rwakeup(&runningrend);
+		qunlock(&runninglock);
+	}
+}
+
+static void
+startprocs(void)
+{
+	int pid;
+
+	golock = calloc(extraproc, sizeof(*golock));
+	if(golock == nil)
+		sysfatal("Could not create go locks: %r\n");
+
+	gorend = calloc(extraproc, sizeof(*gorend));
+	if(gorend == nil)
+		sysfatal("Could not create go rendez: %r\n");
+
+	go = calloc(extraproc, sizeof(*go));
+	if(go == nil)
+		sysfatal("Could not create go flags: %r\n");
+
+	for(pid = 0; pid < extraproc; pid++)
+		gorend[pid].l = golock+pid;
+	runningrend.l = &runninglock;
+
+	for(pid = 0; pid < extraproc; pid++) 
+		proccreate(calcproc, (void*)pid, STK);
+}
+
+/* verlet barnes-hut */
+void
+simulate(void*)
+{
+	Body *b, *s;
+	int nbody, pid;
+	double f;
+
+	threadsetname("simulate");
+
+	startprocs();
+
+	for(;;) {
+		qlock(&glxy);
+
+		if(throttle)
+			sleep(throttle);
+
+		drawglxy();
+
+Again:
+		space.t = EMPTY;
+		quads.l = 0;
+		STATS(quaddepth = 0;)
+		for(b = glxy.a; b < glxy.a + glxy.nb; b++) {
+			if(quadins(b, LIM) == -1) {
+				growquads();
+				goto Again;
+			}
+		}
+
+		running = extraproc;
+		for(pid = 0; pid < extraproc; pid++) {
+			qlock(golock+pid);
+			go[pid] = 1;
+			rwakeup(gorend+pid);
+			qunlock(golock+pid);
+		}
+
+		STATS(avgcalcs = 0;)
+		nbody = glxy.nb / (extraproc+1);
+		s = glxy.a + nbody * (extraproc);
+		for(b = s; b < glxy.a + glxy.nb; b++) {
+			b->a.x = b->newa.x;
+			b->a.y = b->newa.y;
+			b->newa.x = b->newa.y = 0;
+			STATS(calcs = 0;)
+			quadcalc(b, space, LIM);
+			STATS(avgcalcs += calcs;)
+		}
+		STATS(avgcalcs /= glxy.nb;)
+
+		qlock(&runninglock);
+		while(running > 0)
+			rsleep(&runningrend);
+		qunlock(&runninglock);
+
+		for(b = glxy.a; b < glxy.a + glxy.nb; b++) {
+			b->x += dt*b->v.x + dt²*b->a.x/2;
+			b->y += dt*b->v.y + dt²*b->a.y/2;
+			b->v.x += dt*(b->a.x + b->newa.x)/2;
+			b->v.y += dt*(b->a.y + b->newa.y)/2;
+			CHECKLIM(b, f);
+		}
+
+		qunlock(&glxy);
+	}
+}