Skip to content

[RSDK-12819] Cache switching points to bound searches#9

Merged
acmorrow merged 2 commits intoviam-modules:mainfrom
acmorrow:RSDK-12819-sp-caching
Apr 23, 2026
Merged

[RSDK-12819] Cache switching points to bound searches#9
acmorrow merged 2 commits intoviam-modules:mainfrom
acmorrow:RSDK-12819-sp-caching

Conversation

@acmorrow
Copy link
Copy Markdown
Collaborator

We were spending insane amounts of time repeatedly scanning for switching points. Now we cache the results of previous searches and use them to bound the search for the other type. Big win:

Old timings:

2026-04-23T14:51:07.908Z	DEBUG	Viam C++ SDK	[module/ur_arm.cpp:1079] move: phase timing (ms): provisioning=0.36ms preprocessing=0.050000ms validation=0.001000ms segmentation=0.003000ms colinearization=0.001000ms totg_total_gen=0.338000ms legacy_total_gen=0.157000ms	{"log_ts":"2026-04-23T14:51:07.908Z"}
2026-04-23T14:51:13.266Z	DEBUG	Viam C++ SDK	[module/ur_arm.cpp:1079] move: phase timing (ms): provisioning=0.191ms preprocessing=0.012000ms validation=0.000000ms segmentation=0.045000ms colinearization=0.012000ms totg_total_gen=20.793000ms legacy_total_gen=7.561000ms	{"log_ts":"2026-04-23T14:51:13.266Z"}
2026-04-23T14:51:44.022Z	DEBUG	Viam C++ SDK	[module/ur_arm.cpp:1079] move: phase timing (ms): provisioning=0.219ms preprocessing=0.013000ms validation=0.000000ms segmentation=0.028000ms colinearization=0.001000ms totg_total_gen=410.103000ms legacy_total_gen=40.178000ms	{"log_ts":"2026-04-23T14:51:44.022Z"}

New timings:

2026-04-23T14:54:34.718Z	DEBUG	Viam C++ SDK	[module/ur_arm.cpp:1079] move: phase timing (ms): provisioning=0.183ms preprocessing=0.037000ms validation=0.001000ms segmentation=0.005000ms colinearization=0.001000ms totg_total_gen=0.348000ms legacy_total_gen=0.162000ms	{"log_ts":"2026-04-23T14:54:34.718Z"}
2026-04-23T14:54:40.074Z	DEBUG	Viam C++ SDK	[module/ur_arm.cpp:1079] move: phase timing (ms): provisioning=0.174ms preprocessing=0.011000ms validation=0.000000ms segmentation=0.042000ms colinearization=0.011000ms totg_total_gen=20.034000ms legacy_total_gen=7.516000ms	{"log_ts":"2026-04-23T14:54:40.073Z"}
2026-04-23T14:55:10.420Z	DEBUG	Viam C++ SDK	[module/ur_arm.cpp:1079] move: phase timing (ms): provisioning=0.221ms preprocessing=0.014000ms validation=0.000000ms segmentation=0.028000ms colinearization=0.002000ms totg_total_gen=52.239000ms legacy_total_gen=41.496000ms	{"log_ts":"2026-04-23T14:55:10.419Z"}

That's an 8x improvement on that third case.

@acmorrow acmorrow requested a review from nfranczak April 23, 2026 16:59
@acmorrow
Copy link
Copy Markdown
Collaborator Author

@nfranczak - This has an enormous impact on the gp12 regression files:

Old:

replay: success, totg=3528065us, duration=63.4431s, points=71616
./build/local/viam-trajex-totg-replay   3.57s user 0.03s system 92% cpu 3.877 total

Ouch. It took 3.5 seconds!

New:

./build/local/viam-trajex-totg-replay ./src/viam/trajex/totg/test/data/gp12_splice_point_infeasible-20260305.trajex-totg-replay.json
replay: success, totg=44897us, duration=63.4431s, points=71616

Oh cool, it's 45ms.

// Returns the first valid switching point found, or the end of the trajectory if none is found.
//
// Takes path::cursor by value (cheap copy) to seed forward search from current position.
[[gnu::pure]] switching_point find_acceleration_switching_point(path::cursor cursor, const trajectory::options& opt) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bye bye gnu::pure

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't meaningful for these methods the way it was for the math

// Takes path::cursor by value (cheap copy) to seed forward search from current position.
[[gnu::pure]] switching_point find_acceleration_switching_point(path::cursor cursor, const trajectory::options& opt) {
std::optional<switching_point> find_acceleration_switching_point(path::cursor cursor,
arc_length max_search_hint,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search hint caps the range of search?
.. not a huge fan of the word hint here, preference for something more explicit.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If we have a vel sp in front of us, we can stop searching for accel sp's at it, and vice versa. The hint is the last position you need to search to. I called it hint because you are free to search further, and in some cases, we will. For instance, when finding non-diff extrema of the accel limit curve, we will do all of them in the segment we are looking at, and we won't stop at the hint: there are a finite number of non-diff switching points in the segment, and it doesn't do any harm to find them.

If we wanted to go harder at this, we could arrange things so we cached those too, but it didn't seem worth it until we have phase_plane in hand. It does mean that in circular segments we may recompute the same non-diff switching points a few times.

I think that will get fixed when we do RSDK-12891 for real, and go to the unified switching point search. Then all this hinting and caching goes away, because you don't have two search invocations to limit or two results to reconcile.


// No valid switching point found before path end - return end of path as switching point
return switching_point{.point = {.s = cursor.path().length(), .s_dot = arc_velocity{0.0}},
.kind = trajectory::switching_point_kind::k_path_end};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why has this been deleted? This seems correct.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The find_switching_point function handles that now.

return sp1;
}

if (epsilon.wrap(sp1->point.s) == epsilon.wrap(sp2->point.s)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why epsilon wrapped? Seems like small differences matter here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the bad answer is that that is how the old one did it, so I didn't change it. A better answer is that I think you are right that we probably shouldn't do this.

However, my goal with this code is that it not introduce any behavioral changes other than accelerating switching point search, so I'm a little reluctant to take it out.

@acmorrow acmorrow merged commit 8523600 into viam-modules:main Apr 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants