Allow granular progress updates & associated changes#213
Conversation
It's simpler and faster this way
64ba835 to
d7749f7
Compare
We do not want users to call this method, as the Task does not know its listeners (i.e. Progress.update() pings ProgressListeners listening to the Task while Task.update()) does not.
Should be more efficient
While nobody would explicitly do this, it could happen programmatically
2b68394 to
20de674
Compare
Op discovery can take some time - let's let users know something's happening
It is no longer used
We want to propagate progress updates up the chain of subtasks, so tasks must know their parent, at least within the scijava-progress module. To avoid publicly exposing this functionality, though, we make a new method isSubTask which allows us to optionally disregard all subtasks.
20de674 to
ce00177
Compare
There was a problem hiding this comment.
@gselzer Nice PR! Ty for this work adding progress updates to SciJava Ops. This is much appreciated for those long Ops. There a few documentation changes I'd like to see before we merge this into main. I also have some open questions that I'd like to get your thoughts on:
- The progress bar/feedback only really works with Fiji correct? Is there something we can do for users that are using SciJava Ops from Python or some other non-Fiji entry-point?
- The
create.kernelDiffractionOp that creates the PSF for deconvolution takes a little bit of time (not much but its detectable). That might be another good Op to add progress to. - How does this progress mechanism work with external libraries?
I don't have any code suggestions. It looks very clean and well documented to my eye 👍 .
|
|
||
| ``` | ||
|
|
||
| ### Defining Op Progress |
There was a problem hiding this comment.
The Op progress mechanism here described seems to exist primarily for SciJava Ops via Fiji. Can you add a bit about scijava-progress's relationship with Fiji?
|
|
||
| ### Defining Op Progress | ||
|
|
||
| By adding the `scijava-progress` module, your Op can define and update its progress, providing user value for long-running Ops. To add progress to your Op, you must add the following steps to your Op: |
There was a problem hiding this comment.
What kind of feedback, if any, would a user expect to receive from scijava-progress if they're using SciJava Ops via scyjava for example?
There was a problem hiding this comment.
They'd receive no feedback, unless they write their own ProgressListener and register it within the Progress class.
Naively speaking, I'd assume any scijava-ops-python component could harness tqdm to record progress updates, for example.
| /** | ||
| * A simple summer | ||
| * | ||
| * @implNote op names="stats.sum" | ||
| */ | ||
| class DoubleSumOp implements Function<double[], Double> { | ||
| public Double apply(final double[] inArray) { | ||
| Progress.defineTotal(inArray.length); | ||
| double sum = 0; | ||
| for (double v : inArray) { | ||
| sum += v; | ||
| Progress.update(); | ||
| } | ||
| return i; | ||
| } | ||
| } |
There was a problem hiding this comment.
I know this is simple, but can you add comment lines and import Progress like this? It helps catch the eye. For a moment I didn't see that you added the progress bits to this example.
/**
* A simple summer
*
* @implNote op names="stats.sum"
*/
import org.scijava.progress.Progress;
class DoubleSumOp implements Function<double[], Double> {
public Double apply(final double[] inArray) {
// define total progress size
Progress.defineTotal(inArray.length);
double sum = 0;
for (double v : inArray) {
sum += v;
// increment the progress
Progress.update();
}
return i;
}
}| * For each Op dependency that you want to track, pass the Hint `"progress.TRACK"` within the `@OpDependency` annotation. | ||
| * Replace `Progress.defineTotal(long elements)` with `Progress.defineTotal(long elements, long subTasks)`, where `subTasks` is the **total** number of times you will invoke Op dependencies annotated with `"progress.TRACK"`. |
There was a problem hiding this comment.
I'm unclear if the Op dependency itself needs to have Progress.defineTotal() and Progress.update(). This seems to be the case with PadRichardsonLucyTV.java and PadRichardsonLucy.java both tracking the RichardsonLucyC.java (i.e. the core RL Op) with the "progress.TRACK" Op hint. RichardsonLucyC.java itself uses the number of iterations for Progress.defineTotal() and updates the progress.
Will I need to do that for all Op dependencies that I want to track progress?
There was a problem hiding this comment.
Dependencies do not need to define their progress for updates in the dependent Op. If they do not define progress, they will only update the parent progress upon their completion, though.
For example, suppose you have 2 elements worth of updates in your Op, and in between them your Op calls a dependency, which under the hood has 10 elements:
- If that Op reports its progress i.e. calls
Progress.upate(), the dependent Op's progress will go0.0->0.33->0.36->0.39-> ... ->0.63->0.66->1.0 - If that Op does not report its progress the dependent Op's progress will go
0.0->0.33->0.66->1.0
| ``` | ||
|
|
There was a problem hiding this comment.
It might be nice to have a little screen shot of the progress/task manager in Fiji running decon/saca.
| } | ||
| } | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Can you discuss what, if any, performance hit a developer might expect with Progress? Maybe a little section on ideal use versus what a bad use of Progress looks like. I don't think we need to go crazy, but just a flavor.
|
@elevans thanks for the feedback!
There is nothing Fiji-specific about scijava-progress. e.g. Python users could just write a new ...this makes me think that we should probably do away with the
Sure! Do you want to try it?
I'm not really sure what you mean by this. If you're talking about progress updates of libraries that don't depend upon scijava-progress, all Ops record binary progress updates. This means that when you invoke the Op, all If you're talking about how external libraries can register their own Does that help? |
|
Awesome thanks @gselzer for the explanations. I'm happy that you've addressed all my comments. Ready to merge! |
This PR refactors the scijava-progress module API, for the goals of simplicity and better progress reporting within dependencies, and should be squeaked in before the 1.0.0 release.
Important changes:
Tasks instead having a single tracker ofelements, discrete packets of progress updates. The number of elements a Task will have is dictated duringProgress.defineTotal, meaning we no longer need theProgress.setStageMaxmethod.Task.parent(), meaning that if an Op dependency has a dependency itself that issues an update,ProgressListenerstuned to its parent and grandparent will be notified as well. To prevent an overwhelming amount of dependencies from flooding the SciJava Ops Legacy progress listener only posts updates for tasks with no parent."progress.TRACK"hint (i.e.org.scijava.ops.engine.BaseOpHints.Progress.TRACK), and useProgress.defineTotal(x, y), wherexis a long defining the number of elements in the parent Op andyis the total number of reporting dependency executions. E.g.coloc.saca.heatmapZScoreanddeconvolve.richardsonLucy, now report progress.As we are approaching the initial release, we should consider the API - I know @ctrueden wanted to do an API review, so I'm pinging his review as motivation to review the scijava-progress public API. I'm also asking for @elevans' opinion as an Ops developer/user - if you'd like to try your hand at adding progress to fully test the API, I'd appreciate it, but if you're busy no worries.
Points of Discussion:
LoopBuilderconstructs such that pixel-wise Ops always report progress when lifted to image level, however this anecdotally incurs very poor performance due to the constant multithreaded updates to theAtomicLongtracking progress. @tpietzsch is there anything that we could do, here or upstream in ImgLib2, to enable progress reporting? Potentially one update per chunk would be awesome - would it be possible to add API to report chunk size, for example?TODO: