|
1 // |
|
2 // NBody adapted from Intel's nbody benchmark. |
|
3 // |
|
4 |
|
5 load(libdir + "util.js"); |
|
6 load(libdir + "seedrandom.js"); |
|
7 |
|
8 var NBody = { |
|
9 Constant: { |
|
10 "deltaTime": 1, // 0.005 in their code. |
|
11 "epsSqr": 50, // softening factor, when they compute, set to 50. |
|
12 "initialVelocity": 8 // set to 0 to turn off |
|
13 }, |
|
14 |
|
15 init: function init(mode, numBodies) { |
|
16 var initPos = new Array(numBodies); |
|
17 var initVel = new Array(numBodies); |
|
18 |
|
19 // initialization of inputs |
|
20 for (var i = 0; i < numBodies; i++) { |
|
21 // [x,y,z] |
|
22 initPos[i] = [Math.floor((Math.random()) * 40000), |
|
23 Math.floor((Math.random()) * 20000), |
|
24 Math.floor((Math.random() - .25) * 50000)]; |
|
25 |
|
26 // [x,y,z,x,y,z] |
|
27 initVel[i] = [(Math.random() - 0.5) * NBody.Constant.initialVelocity, |
|
28 (Math.random() - 0.5) * NBody.Constant.initialVelocity, |
|
29 (Math.random()) * NBody.Constant.initialVelocity + 10, |
|
30 |
|
31 (Math.random() - 0.5) * NBody.Constant.initialVelocity, |
|
32 (Math.random() - 0.5) * NBody.Constant.initialVelocity, |
|
33 (Math.random()) * NBody.Constant.initialVelocity]; |
|
34 } |
|
35 |
|
36 NBody.private = {}; |
|
37 |
|
38 if (mode === "par") { |
|
39 NBody.private.pos = new ParallelArray(initPos); |
|
40 NBody.private.vel = new ParallelArray(initVel); |
|
41 } else { |
|
42 NBody.private.pos = initPos; |
|
43 NBody.private.vel = initVel; |
|
44 } |
|
45 |
|
46 NBody.numBodies = numBodies; |
|
47 NBody.time = 0; |
|
48 }, |
|
49 |
|
50 // Parallel |
|
51 |
|
52 tickPar: function tickPar() { |
|
53 NBody.private.vel = new ParallelArray([NBody.numBodies], NBody.velocityPar); |
|
54 NBody.private.pos = new ParallelArray([NBody.numBodies], NBody.positionPar); |
|
55 NBody.time++; |
|
56 }, |
|
57 |
|
58 velocityPar: function velocityPar(index) { |
|
59 var pos = NBody.private.pos; |
|
60 var vel = NBody.private.vel; |
|
61 |
|
62 var deltaTime = NBody.Constant.deltaTime; |
|
63 var epsSqr = NBody.Constant.epsSqr; |
|
64 var time = NBody.time; |
|
65 |
|
66 var shape = vel.shape[0]; |
|
67 |
|
68 var newVel; |
|
69 var newX, newY, newZ; |
|
70 var newX2, newY2, newZ2; |
|
71 |
|
72 var cX = Math.cos(time / 22) * -4200; |
|
73 var cY = Math.sin(time / 14) * 9200; |
|
74 var cZ = Math.sin(time / 27) * 6000; |
|
75 |
|
76 // pull to center |
|
77 var maxDistance = 3400; |
|
78 var pullStrength = .042; |
|
79 |
|
80 var speedLimit = 8; |
|
81 |
|
82 // zones |
|
83 var zone = 400; |
|
84 var repel = 100; |
|
85 var align = 300; |
|
86 var attract = 100; |
|
87 |
|
88 if (time < 500) { |
|
89 speedLimit = 2000; |
|
90 var attractPower = 100.9; |
|
91 } else { |
|
92 speedLimit = .2; |
|
93 attractPower = 20.9; |
|
94 } |
|
95 |
|
96 var zoneSqrd = zone * zone + zone * zone + zone * zone; |
|
97 |
|
98 var accX = 0, accY = 0, accZ = 0; |
|
99 var accX2 = 0, accY2 = 0, accZ2 = 0; |
|
100 var i; |
|
101 |
|
102 // define particle 1 center distance |
|
103 var dirToCenterX = cX - pos.get(index)[0]; |
|
104 var dirToCenterY = cY - pos.get(index)[1]; |
|
105 var dirToCenterZ = cZ - pos.get(index)[2]; |
|
106 |
|
107 var distanceSquaredTo = dirToCenterX * dirToCenterX + dirToCenterY * dirToCenterY + dirToCenterZ * dirToCenterZ; |
|
108 var distToCenter = Math.sqrt(distanceSquaredTo); |
|
109 |
|
110 // orient to center |
|
111 if (distToCenter > maxDistance) { |
|
112 var velc = (distToCenter - maxDistance) * pullStrength; |
|
113 if (time < 200) |
|
114 velc = .2; |
|
115 else velc = (distToCenter - maxDistance) * pullStrength; |
|
116 |
|
117 accX += (dirToCenterX / distToCenter) * velc; |
|
118 accY += (dirToCenterY / distToCenter) * velc; |
|
119 accZ += (dirToCenterZ / distToCenter) * velc; |
|
120 } |
|
121 |
|
122 for (i = 0; i < shape; i = i + 1) { |
|
123 var rx = pos.get(i)[0] - pos.get(index)[0]; |
|
124 var ry = pos.get(i)[1] - pos.get(index)[1]; |
|
125 var rz = pos.get(i)[2] - pos.get(index)[2]; |
|
126 |
|
127 // make sure we are not testing the particle against its own position |
|
128 var areSame = 0; |
|
129 if (pos.get(i)[0] == pos.get(index)[0] && pos.get(i)[1] == pos.get(index)[1] && pos.get(i)[2] == pos.get(index)[2]) |
|
130 areSame += 1; |
|
131 |
|
132 var distSqrd = rx * rx + ry * ry + rz * rz; |
|
133 |
|
134 // cant use eqals to test, only <= or >= WTF |
|
135 if (distSqrd < zoneSqrd && areSame <= 0) { |
|
136 var length = Math.sqrt(distSqrd); |
|
137 var percent = distSqrd / zoneSqrd; |
|
138 |
|
139 if (distSqrd < repel) { |
|
140 var F = (repel / percent - 1) * .025; |
|
141 |
|
142 var normalRx = (rx / length) * F; |
|
143 var normalRy = (ry / length) * F; |
|
144 var normalRz = (rz / length) * F; |
|
145 |
|
146 accX = accX + normalRx; |
|
147 accY = accY + normalRy; |
|
148 accZ = accZ + normalRz; |
|
149 |
|
150 accX2 = accX2 - normalRx; |
|
151 accY2 = accY2 - normalRy; |
|
152 accZ2 = accZ2 - normalRz; |
|
153 } else if (distSqrd < align) { //align |
|
154 var threshDelta = align - repel; |
|
155 var adjustedPercent = (percent - repel) / threshDelta; |
|
156 var Q = (.5 - Math.cos(adjustedPercent * 3.14159265 * 2) * .5 + .5) * 100.9; |
|
157 |
|
158 // get velocity 2 |
|
159 var velX2 = vel.get(i)[4]; |
|
160 var velY2 = vel.get(i)[5]; |
|
161 var velZ2 = vel.get(i)[6]; |
|
162 |
|
163 var velLength2 = Math.sqrt(velX2 * velX2 + velY2 * velY2 + velZ2 * velZ2); |
|
164 |
|
165 // normalize vel2 and multiply by factor |
|
166 velX2 = (velX2 / velLength2) * Q; |
|
167 velY2 = (velY2 / velLength2) * Q; |
|
168 velZ2 = (velZ2 / velLength2) * Q; |
|
169 |
|
170 // get own velocity |
|
171 var velX = vel.get(i)[0]; |
|
172 var velY = vel.get(i)[1]; |
|
173 var velZ = vel.get(i)[2]; |
|
174 |
|
175 var velLength = Math.sqrt(velX * velX + velY * velY + velZ * velZ); |
|
176 |
|
177 // normalize own velocity |
|
178 velX = (velX / velLength) * Q; |
|
179 velY = (velY / velLength) * Q; |
|
180 velZ = (velZ / velLength) * Q; |
|
181 |
|
182 accX += velX2; |
|
183 accY += velY2; |
|
184 accZ += velZ2; |
|
185 |
|
186 accX2 += velX; |
|
187 accY2 += velY; |
|
188 accZ2 += velZ; |
|
189 } |
|
190 |
|
191 if (distSqrd > attract) { // attract |
|
192 var threshDelta2 = 1 - attract; |
|
193 var adjustedPercent2 = (percent - attract) / threshDelta2; |
|
194 var C = (1 - (Math.cos(adjustedPercent2 * 3.14159265 * 2) * 0.5 + 0.5)) * attractPower; |
|
195 |
|
196 // normalize the distance vector |
|
197 var dx = (rx / (length)) * C; |
|
198 var dy = (ry / (length)) * C; |
|
199 var dz = (rz / (length)) * C; |
|
200 |
|
201 accX += dx; |
|
202 accY += dy; |
|
203 accZ += dz; |
|
204 |
|
205 accX2 -= dx; |
|
206 accY2 -= dy; |
|
207 accZ2 -= dz; |
|
208 } |
|
209 } |
|
210 } |
|
211 |
|
212 // Speed limits |
|
213 if (time > 500) { |
|
214 var accSquared = accX * accX + accY * accY + accZ * accZ; |
|
215 if (accSquared > speedLimit) { |
|
216 accX = accX * .015; |
|
217 accY = accY * .015; |
|
218 accZ = accZ * .015; |
|
219 } |
|
220 |
|
221 var accSquared2 = accX2 * accX2 + accY2 * accY2 + accZ2 * accZ2; |
|
222 if (accSquared2 > speedLimit) { |
|
223 accX2 = accX2 * .015; |
|
224 accY2 = accY2 * .015; |
|
225 accZ2 = accZ2 * .015; |
|
226 } |
|
227 } |
|
228 |
|
229 // Caclulate new velocity |
|
230 newX = (vel.get(index)[0]) + accX; |
|
231 newY = (vel.get(index)[1]) + accY; |
|
232 newZ = (vel.get(index)[2]) + accZ; |
|
233 |
|
234 newX2 = (vel.get(index)[3]) + accX2; |
|
235 newY2 = (vel.get(index)[4]) + accY2; |
|
236 newZ2 = (vel.get(index)[5]) + accZ2; |
|
237 |
|
238 if (time < 500) { |
|
239 var acs = newX2 * newX2 + newY2 * newY2 + newZ2 * newZ2; |
|
240 if (acs > speedLimit) { |
|
241 newX2 = newX2 * .15; |
|
242 newY2 = newY2 * .15; |
|
243 newZ2 = newZ2 * .15; |
|
244 } |
|
245 |
|
246 var acs2 = newX * newX + newY * newY + newZ * newZ; |
|
247 if (acs2 > speedLimit) { |
|
248 newX = newX * .15; |
|
249 newY = newY * .15; |
|
250 newZ = newZ * .15; |
|
251 } |
|
252 } |
|
253 |
|
254 return [newX, newY, newZ, newX2, newY2, newZ2]; |
|
255 }, |
|
256 |
|
257 positionPar: function positionPar(index) { |
|
258 var vel = NBody.private.vel; |
|
259 var pos = NBody.private.pos; |
|
260 |
|
261 var x = 0; |
|
262 var y = 0; |
|
263 var z = 0; |
|
264 |
|
265 var velX = vel.get(index)[0]; |
|
266 var velY = vel.get(index)[1]; |
|
267 var velZ = vel.get(index)[2]; |
|
268 |
|
269 var velX2 = vel.get(index)[3]; |
|
270 var velY2 = vel.get(index)[4]; |
|
271 var velZ2 = vel.get(index)[5]; |
|
272 |
|
273 var netVelX = (velX - velX2); |
|
274 var netVelY = (velY - velY2); |
|
275 var netVelZ = (velZ - velZ2); |
|
276 |
|
277 x = pos.get(index)[0] + (netVelX); |
|
278 y = pos.get(index)[1] + (netVelY); |
|
279 z = pos.get(index)[2] + (netVelZ); |
|
280 |
|
281 return [x, y, z]; |
|
282 }, |
|
283 |
|
284 // Sequential |
|
285 |
|
286 tickSeq: function tickSeq() { |
|
287 var numBodies = NBody.numBodies; |
|
288 var newVel = new Array(numBodies); |
|
289 var newPos = new Array(numBodies); |
|
290 |
|
291 for (var i = 0; i < numBodies; i++) |
|
292 newVel[i] = NBody.velocitySeq(i); |
|
293 |
|
294 for (var i = 0; i < numBodies; i++) |
|
295 newPos[i] = NBody.positionSeq(i); |
|
296 |
|
297 NBody.private.vel = newVel; |
|
298 NBody.private.pos = newPos; |
|
299 |
|
300 NBody.time++; |
|
301 }, |
|
302 |
|
303 velocitySeq: function velocitySeq(index) { |
|
304 var vel = NBody.private.vel; |
|
305 var pos = NBody.private.pos; |
|
306 |
|
307 var deltaTime = NBody.Constant.deltaTime; |
|
308 var epsSqr = NBody.Constant.epsSqr; |
|
309 var time = NBody.time; |
|
310 |
|
311 var shape = pos.length; |
|
312 |
|
313 var newVel; |
|
314 var newX, newY, newZ; |
|
315 var newX2, newY2, newZ2; |
|
316 |
|
317 var cX = Math.cos(time / 22) * -4200; |
|
318 var cY = Math.sin(time / 14) * 9200; |
|
319 var cZ = Math.sin(time / 27) * 6000; |
|
320 |
|
321 // pull to center |
|
322 var maxDistance = 3400; |
|
323 var pullStrength = .042; |
|
324 |
|
325 var speedLimit = 8; |
|
326 |
|
327 // zones |
|
328 var zone = 400; |
|
329 var repel = 100; |
|
330 var align = 300; |
|
331 var attract = 100; |
|
332 |
|
333 |
|
334 if (time < 500) { |
|
335 speedLimit = 2000; |
|
336 var attractPower = 100.9; |
|
337 } else { |
|
338 speedLimit = .2; |
|
339 attractPower = 20.9; |
|
340 } |
|
341 |
|
342 var zoneSqrd = zone * zone + zone * zone + zone * zone; |
|
343 |
|
344 var accX = 0, accY = 0, accZ = 0; |
|
345 var accX2 = 0, accY2 = 0, accZ2 = 0; |
|
346 var i; |
|
347 |
|
348 // define particle 1 center distance |
|
349 var dirToCenterX = cX - pos[index][0]; |
|
350 var dirToCenterY = cY - pos[index][1]; |
|
351 var dirToCenterZ = cZ - pos[index][2]; |
|
352 |
|
353 var distanceSquaredTo = dirToCenterX * dirToCenterX + dirToCenterY * dirToCenterY + dirToCenterZ * dirToCenterZ; |
|
354 var distToCenter = Math.sqrt(distanceSquaredTo); |
|
355 |
|
356 // orient to center |
|
357 if (distToCenter > maxDistance) { |
|
358 var velc = (distToCenter - maxDistance) * pullStrength; |
|
359 if (time < 200) |
|
360 velc = .2; |
|
361 else velc = (distToCenter - maxDistance) * pullStrength; |
|
362 |
|
363 accX += (dirToCenterX / distToCenter) * velc; |
|
364 accY += (dirToCenterY / distToCenter) * velc; |
|
365 accZ += (dirToCenterZ / distToCenter) * velc; |
|
366 } |
|
367 |
|
368 for (i = 0; i < shape; i = i + 1) { |
|
369 var rx = pos[i][0] - pos[index][0]; |
|
370 var ry = pos[i][1] - pos[index][1]; |
|
371 var rz = pos[i][2] - pos[index][2]; |
|
372 |
|
373 var areSame = 0; |
|
374 if (pos[i][0] == pos[index][0] && pos[i][1] == pos[index][1] && pos[i][2] == pos[index][2]) |
|
375 areSame += 1; |
|
376 |
|
377 var distSqrd = rx * rx + ry * ry + rz * rz; |
|
378 |
|
379 // cant use eqals to test, only <= or >= WTF |
|
380 if (distSqrd < zoneSqrd && areSame <= 0) { |
|
381 var length = Math.sqrt(distSqrd); |
|
382 var percent = distSqrd / zoneSqrd; |
|
383 |
|
384 |
|
385 if (distSqrd < repel) { |
|
386 var F = (repel / percent - 1) * .025; |
|
387 |
|
388 var normalRx = (rx / length) * F; |
|
389 var normalRy = (ry / length) * F; |
|
390 var normalRz = (rz / length) * F; |
|
391 |
|
392 accX = accX + normalRx; |
|
393 accY = accY + normalRy; |
|
394 accZ = accZ + normalRz; |
|
395 |
|
396 accX2 = accX2 - normalRx; |
|
397 accY2 = accY2 - normalRy; |
|
398 accZ2 = accZ2 - normalRz; |
|
399 } else if (distSqrd < align) { //align |
|
400 var threshDelta = align - repel; |
|
401 var adjustedPercent = (percent - repel) / threshDelta; |
|
402 var Q = (.5 - Math.cos(adjustedPercent * 3.14159265 * 2) * .5 + .5) * 100; |
|
403 |
|
404 // get velocity 2 |
|
405 var velX2 = vel[i][3]; |
|
406 var velY2 = vel[i][4]; |
|
407 var velZ2 = vel[i][5]; |
|
408 |
|
409 var velLength2 = Math.sqrt(velX2 * velX2 + velY2 * velY2 + velZ2 * velZ2); |
|
410 |
|
411 // normalize vel2 and multiply by factor |
|
412 velX2 = (velX2 / velLength2) * Q; |
|
413 velY2 = (velY2 / velLength2) * Q; |
|
414 velZ2 = (velZ2 / velLength2) * Q; |
|
415 |
|
416 // get own velocity |
|
417 var velX = vel[i][0]; |
|
418 var velY = vel[i][1]; |
|
419 var velZ = vel[i][2]; |
|
420 |
|
421 var velLength = Math.sqrt(velX * velX + velY * velY + velZ * velZ); |
|
422 |
|
423 // normalize own velocity |
|
424 velX = (velX / velLength) * Q; |
|
425 velY = (velY / velLength) * Q; |
|
426 velZ = (velZ / velLength) * Q; |
|
427 |
|
428 accX += velX2; |
|
429 accY += velY2; |
|
430 accZ += velZ2; |
|
431 |
|
432 accX2 += velX; |
|
433 accY2 += velY; |
|
434 accZ2 += velZ; |
|
435 } |
|
436 |
|
437 if (distSqrd > attract) { //attract |
|
438 var threshDelta2 = 1 - align; |
|
439 var adjustedPercent2 = (percent - align) / threshDelta2; |
|
440 var C = (1 - (Math.cos(adjustedPercent2 * 3.14159265 * 2) * 0.5 + 0.5)) * attractPower; |
|
441 |
|
442 // normalize the distance vector |
|
443 var dx = (rx / (length)) * C; |
|
444 var dy = (ry / (length)) * C; |
|
445 var dz = (rz / (length)) * C; |
|
446 |
|
447 debug = 1.1; |
|
448 |
|
449 accX += dx; |
|
450 accY += dy; |
|
451 accZ += dz; |
|
452 |
|
453 accX2 -= dx; |
|
454 accY2 -= dy; |
|
455 accZ2 -= dz; |
|
456 } |
|
457 } |
|
458 } |
|
459 |
|
460 // enforce speed limits |
|
461 if (time > 500) { |
|
462 var accSquared = accX * accX + accY * accY + accZ * accZ; |
|
463 if (accSquared > speedLimit) { |
|
464 accX = accX * .015; |
|
465 accY = accY * .015; |
|
466 accZ = accZ * .015; |
|
467 } |
|
468 |
|
469 var accSquared2 = accX2 * accX2 + accY2 * accY2 + accZ2 * accZ2; |
|
470 if (accSquared2 > speedLimit) { |
|
471 accX2 = accX2 * .015; |
|
472 accY2 = accY2 * .015; |
|
473 accZ2 = accZ2 * .015; |
|
474 } |
|
475 } |
|
476 |
|
477 // Caclulate new velocity |
|
478 newX = vel[index][0] + accX; |
|
479 newY = vel[index][1] + accY; |
|
480 newZ = vel[index][2] + accZ; |
|
481 |
|
482 newX2 = vel[index][3] + accX2; |
|
483 newY2 = vel[index][4] + accY2; |
|
484 newZ2 = vel[index][5] + accZ2; |
|
485 |
|
486 if (time < 500) { |
|
487 var acs = newX2 * newX2 + newY2 * newY2 + newZ2 * newZ2; |
|
488 if (acs > speedLimit) { |
|
489 newX2 = newX2 * .15; |
|
490 newY2 = newY2 * .15; |
|
491 newZ2 = newZ2 * .15; |
|
492 } |
|
493 |
|
494 var acs2 = newX * newX + newY * newY + newZ * newZ; |
|
495 if (acs2 > speedLimit) { |
|
496 newX = newX * .15; |
|
497 newY = newY * .15; |
|
498 newZ = newZ * .15; |
|
499 } |
|
500 } |
|
501 |
|
502 return [newX, newY, newZ, newX2, newY2, newZ2]; |
|
503 }, |
|
504 |
|
505 positionSeq: function positionSeq(index) { |
|
506 var vel = NBody.private.vel; |
|
507 var pos = NBody.private.pos; |
|
508 |
|
509 var x = 0; |
|
510 var y = 0; |
|
511 var z = 0; |
|
512 |
|
513 var velX = vel[index][0]; |
|
514 var velY = vel[index][1]; |
|
515 var velZ = vel[index][2]; |
|
516 |
|
517 var velX2 = vel[index][3]; |
|
518 var velY2 = vel[index][4]; |
|
519 var velZ2 = vel[index][5]; |
|
520 |
|
521 var netX = (velX - velX2); |
|
522 var netY = (velY - velY2); |
|
523 var netZ = (velZ - velZ2); |
|
524 |
|
525 x = pos[index][0] + netX; |
|
526 y = pos[index][1] + netY; |
|
527 z = pos[index][2] + netZ; |
|
528 |
|
529 return [x, y, z]; |
|
530 } |
|
531 }; |
|
532 |
|
533 function emulateNBody(mode, numBodies, ticks) { |
|
534 NBody.init(mode, numBodies); |
|
535 for (var i = 0; i < ticks; i++) { |
|
536 var start = Date.now(); |
|
537 if (mode === "par") |
|
538 NBody.tickPar(); |
|
539 else |
|
540 NBody.tickSeq(); |
|
541 //print(NBody.private.pos); |
|
542 print(mode + " bodies=" + numBodies + " tick=" + (i+1) + "/" + ticks + ": " + (Date.now() - start) + " ms"); |
|
543 } |
|
544 } |
|
545 |
|
546 // Using 4000 bodies going off Rick's comment as 4000 being a typical workload. |
|
547 const NUMBODIES = 4000; |
|
548 const TICKS = 10; |
|
549 |
|
550 Math.seedrandom("seed"); |
|
551 |
|
552 benchmark("NBODY", 1, DEFAULT_MEASURE, |
|
553 function () { emulateNBody("seq", NUMBODIES, TICKS); }, |
|
554 function () { emulateNBody("par", NUMBODIES, TICKS); }); |