-
Notifications
You must be signed in to change notification settings - Fork 5
feat(speculation): generator contract and bestfirst impl #446
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
Open
behinddwalls
wants to merge
1
commit into
preetam/speculation-speculator
from
preetam/speculation-generator
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| load("@rules_go//go:def.bzl", "go_library") | ||
|
|
||
| go_library( | ||
| name = "go_default_library", | ||
| srcs = ["generator.go"], | ||
| importpath = "github.com/uber/submitqueue/submitqueue/extension/speculation/generator", | ||
| visibility = ["//visibility:public"], | ||
| deps = ["//submitqueue/entity:go_default_library"], | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # generator | ||
|
|
||
| The `generator` package is a piece the `standard` `Speculator` is built from: a `Generator` produces the queue's candidate paths as one stream, best first, across all heads. It is **not** controller-facing — the speculate controller only knows the `Speculator` contract, and a different `Speculator` need not split its work this way. So there is no `Config` or `Factory` here; a `Generator` is chosen when the `standard` `Speculator` is constructed. | ||
|
|
||
| `Open` starts the stream over the queue's live batches and their path sets and returns a `PathIterator`. The caller pulls one candidate at a time and the generator does only the work that answer needs. Candidates descend in score, never repeat, and never contradict a known fact. A candidate's score means something only for the current run; scores are never stored. | ||
|
|
||
| ## `bestfirst` | ||
|
|
||
| A head waiting on unfinished dependencies has one path per combination of guesses — 2ⁿ of them — while callers want the best few. `bestfirst` hands them out in score order without building the rest. | ||
|
|
||
| Dependencies that already finished are not guesses: landed means included, failed or cancelled means excluded. They stay in the path but drop out of the search. Each unfinished one is a two-way guess whose chance of landing comes from an injected `scorer.Scorer`, remembered per dependency so one shared by several heads is scored once; a dependency that is not a live batch is treated as a coin flip. A path's score is its guesses' chances multiplied together. Heads with more unfinished dependencies than the depth bound are skipped until some resolve, and paths that already finished are passed over — so a failed path drops out and the next-best takes its place. | ||
|
|
||
| **The best path is not "bet everything lands."** Each guess takes the *likelier* outcome, so a dependency that will probably fail is excluded. Every other path flips some of those guesses, and each flip costs a known factor. One consequence worth knowing: flipping two cheap guesses can beat flipping one expensive guess, and the ordering handles that correctly. | ||
|
|
||
| **Laziness at two levels.** Across heads, a heap holds each head's current offer — a real, already-built path, never an estimate — so the top of the heap is the best path in the queue. Within a head the same shape repeats: it keeps a small heap, hands out its best, and builds only the one or two paths that come after it. Pulling *k* candidates builds about *k* paths, whatever the size of the set behind them. | ||
|
|
||
| `bestfirst` discards nothing: pull long enough and every path within the depth bound comes out. It does no conflict relaxation (`dropped` bets) — that is the `Speculator`'s call, not the generator's. | ||
|
|
||
| The ordering trick behind all this — how the walk reaches every path exactly once, in score order, without tracking what it has already seen — is documented with a worked example on `expand` in `bestfirst.go`. The behavior is pinned by tests in `bestfirst_test.go`. | ||
26 changes: 26 additions & 0 deletions
26
submitqueue/extension/speculation/generator/bestfirst/BUILD.bazel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| load("@rules_go//go:def.bzl", "go_library", "go_test") | ||
|
|
||
| go_library( | ||
| name = "go_default_library", | ||
| srcs = ["bestfirst.go"], | ||
| importpath = "github.com/uber/submitqueue/submitqueue/extension/speculation/generator/bestfirst", | ||
| visibility = ["//visibility:public"], | ||
| deps = [ | ||
| "//submitqueue/entity:go_default_library", | ||
| "//submitqueue/extension/scorer:go_default_library", | ||
| "//submitqueue/extension/speculation/generator:go_default_library", | ||
| ], | ||
| ) | ||
|
|
||
| go_test( | ||
| name = "go_default_test", | ||
| srcs = ["bestfirst_test.go"], | ||
| embed = [":go_default_library"], | ||
| deps = [ | ||
| "//submitqueue/entity:go_default_library", | ||
| "//submitqueue/extension/scorer:go_default_library", | ||
| "//submitqueue/extension/speculation/generator:go_default_library", | ||
| "@com_github_stretchr_testify//assert:go_default_library", | ||
| "@com_github_stretchr_testify//require:go_default_library", | ||
| ], | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
why need a depth bound? Should it be simply guarded by build budget?