Put this pet project on the backburner for a while and got back into it last night.
After getting CPU multithreading working I decided to figure out how to use the GPU for potentially massive parallelization gains. There are several different ways of doing this, but I decided the best way was to use Compute Shaders, as Unity already has built-in functionality with them and will compile them to work on many different GPU's. After some bumpy starts I managed to get it to work, but the performance gain was barely 2x at best. After some investigation I learned two things:
1. GPU's REALLY don't like loops.
2. There's some weird truncation or precision errors that I have to deal with.
I first had my particle-mover loop in a C# script, but I think that I was getting bottlenecked by buffer reads and writes. I then moved the loop to inside the compute shader. I saw little to any performance gain even doing this. After a bit of research, I discovered I could 'unroll' the loop inside the shader (basically removing the loop and duplicating each statement inside the loop once for each iteration). Unrolling the loop to 100 iterations gave me significant performance gains, but any larger and the shader became too large to compile quickly. So, I added another loop and nested the unrolled loop inside this one, which remains 'rolled'.
Wow... with 100 external loop iterations and an internal loop 'unrolled' to 100 iterations for a combined 10,000 calculation steps per frame, total calculation time was about 0.004 ms, or 4 microseconds. Compare this to my best CPU multithread time of 0.3 ms per step. This is roughly a 75x gain.
Now for something weirdly funny. I guess the GPU does things a bit differently from the CPU when it comes to precision. I ran into the issue that a population of electrons moving in a homogenous magnetic field will quickly sort themselves into 'sheets' or 'layers' at roughly powers of two ratios from each other in distance along the field lines, with a handful of outliers that don't fit into the ratio. See the images below:
First image is electrons in their initial positions. All electrons are within 1 unit (meter) of the origin along each axis. A homogenous magnetic field with a vector of [0,0,1] is applied, which points in the direction of the blue arrow here in the Unity editor. The electrons quickly move along the z-axis until they sort themselves into the layers shown in the bottom image. They don't stop moving completely, they still gyrate around the magnetic field lines as if they were moving along the z-axis, but they don't actually move along that axis any longer.
The layers have a z-value of positive and negative 4, 2, 1, 0.5, and 0.25, with a few in between once you get close to the origin.
I haven't really even begun to investigate this phenomenon, as I thought it was too funny and interesting to wait to share.