Hey @christopher6
Off the top of my head a few things that would make this slow:
- The component executes the same number of times that there are mesh vertices
- The component needs to output the number of curves * the number of vertices points
- Even though each check is exclusive, they are run in parallel
Other improvements could be using a spatial data structure (4) (KD-Tree or OcTree) to remove the need for each point to check each curve, but let’s try without first.
To reduce (1), using a C# component with list/list access (rather than the default component which has item/list access) should be much faster.
To reduce (2), we can only output the data needed by performing some sorting in the component. In addition to this, we can cast directly to the type we need as the GH cast mechanisms are a little slow.
And for (3) we can perform it in parallel.
Implementing (1) and (2)
As a C# component
private void RunScript(List<Point3d> points, List<Curve> curves, ref object distance)
{
List<GH_Number> results = new List<GH_Number>();
foreach (Point3d p in points)
{
double closestDistance = double.MaxValue;
foreach (Curve c in curves) {
double param;
if (c.ClosestPoint(p, out param)) {
double dist = c.PointAt(param).DistanceTo(p);
if (dist < closestDistance) {
closestDistance = dist;
}
}
}
results.Add(new GH_Number(closestDistance));
}
distance = results;
}
2 Curves about 10-15% faster
5 Curves about 25-30% faster,
Implementing (3): running in parallel
A slight refactoring to use arrays and indexing the points lets us run in parallel
private void RunScript(List<Point3d> points, List<Curve> curves, ref object distance)
{
GH_Number[] results = new GH_Number[points.Count];
System.Threading.Tasks.Parallel.For(0, points.Count, (index) => {
Point3d p = points[index];
double closestDistance = double.MaxValue;
foreach (Curve c in curves) {
double param;
if (c.ClosestPoint(p, out param)) {
double dist = c.PointAt(param).DistanceTo(p);
if (dist < closestDistance) {
closestDistance = dist;
}
}
}
results[index] = new GH_Number(closestDistance);
});
distance = results;
}
2 Curves about 2x faster
5 Curves about 5x faster,
120 curves about 40x faster!
Discussion
So computing multiple curves takes a similar amount of time to computing a singular one as we distribute the load across multiple threads. This ends up diminishing slightly when we are computing for more curves than we have processor threads, which is why we see a 1:1 improvement with 5 curves, but a 1:3 improvement with 120 curves.
On my Ryzen 9 3900X (12 core/24 thread) these are results for comparison with the base Curve CP component:
Plus we can then remove the expensive sorting that comes after (another 20s).
About 40x faster (due to (1) (2) and (3) not just the multithreading!
Attached:
210904_test script_cn.gh (11.4 KB)