-
Notifications
You must be signed in to change notification settings - Fork 6
Improve multi stop plan unit generation #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
eb7497f
to
c21c0cf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In which state is this? 🤔
@merschformann I want to do some further benchmarking. I'll ping you all again once that is done |
1466b49
to
fcbc3d3
Compare
🤔 linter |
Waiting on #47 to fix some benchmark tests |
fcbc3d3
to
78decc8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Just left some thoughts, nothing blocking at all. Should we finally merge this?
solution_move_stops_generator.go
Outdated
@@ -109,10 +113,13 @@ func SolutionMoveStopsGenerator( | |||
} | |||
|
|||
// TODO: we can reuse the stopPositions slice from m |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can remove the TODO now, right?
solution_sequence_generator.go
Outdated
) | ||
} | ||
|
||
func recSequenceGenerator( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can leave a comment on the func or fully write out the rec as recursive? I was confused for a minute trying to understand what this func now records. 😅
Or am I missing a point here? 🤔
solution_sequence_generator.go
Outdated
if len(sequence) == len(stops) { | ||
if atomic.AddInt64(maxSequences, -1) >= 0 { | ||
yield(slices.Clone(sequence)) | ||
nStops := len(stops) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, this can be moved down a bit, right? Not that it will matter much though. 😹
stopOrder = stopOrder[:1] | ||
stopOrder[0] = stopIdx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It kind of makes sense that this is faster (I assume). I wonder whether it would make sense to introduce a helper func to make the intention more obvious. Something like:
func recycleSlice(s *[]int, n int) {
*s = (*s)[:n]
}
Then again, this may defeat the purpose. 😅
Adding comments all over the place like this is probably also not great:
// we recycle the slice instead of creating a new one for performance reasons
stopOrder = stopOrder[:1]
stopOrder[0] = stopIdx
I suppose in a performance driven code-base it's fine like it is. What do you think @dirkschumacher ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just FYI: a quick benchmark on the func approach yields this to me, but double check me as I am a bit side-tracked right now:
goos: linux
goarch: amd64
pkg: xxx
cpu: AMD Ryzen 7 5800H with Radeon Graphics
BenchmarkRecycleInline-16 1000000000 1.877 ns/op 8 B/op 0 allocs/op
BenchmarkRecycleFunction-16 1000000000 1.164 ns/op 8 B/op 0 allocs/op
BenchmarkRecycleNew-16 91827982 12.27 ns/op 16 B/op 1 allocs/op
PASS
Based on this code:
package recycle_slice_test
import "testing"
func recycleSlice(s *[]int, n int) {
*s = (*s)[:n]
}
func BenchmarkRecycleInline(b *testing.B) {
slice := make([]int, b.N)
value := 20
for i := 0; i < b.N; i++ {
slice = slice[:1]
slice[0] = value
}
}
func BenchmarkRecycleFunction(b *testing.B) {
slice := make([]int, b.N)
value := 20
for i := 0; i < b.N; i++ {
recycleSlice(&slice, 1)
slice[0] = value
}
}
func BenchmarkRecycleNew(b *testing.B) {
slice := make([]int, b.N)
value := 20
for i := 0; i < b.N; i++ {
slice = make([]int, 1)
slice[0] = value
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally find
stopOrder = stopOrder[:1]
stopOrder[0] = stopIdx
relatively clear without a comment.
Explored a bit to reduce some overhead when generating sequences for move generation. In addition this has the effect that we don't spawn go routines so deep in the search.
Below are two smaller benchmark tests. The full set I cannot yet run until #47 is fixed, but this already captures some of the benefits here as it's mostly about allocations.