Conversation
|
Using Logging instead of Debug in naming is totally fine. Debug might only be one reason one wants to see prints :) |
|
My system also only very slowly evolved into the current state, so I do see that starting that anew from scratch might look like quite some work. I can take a look at the sub solver ideas once you have settled your ideas a bit as well as the factory. About the “what-to-warp” I think that is a state-property to know wether to log or not; neither problem nor algorithm are changed. The Manopt subsolver debug there is the “sending side” as well as the “receiving side” and sure that wraps a certain debugger inside. - and the communicate via the |
|
So, while it took me some time to actually write some updates, this has actually been in the back of my mind for quite some time. One of the main reasons I was drawn to the logging system is that it does not require any wrapping, and I was hoping to find a way to not have to pollute the actual algorithms too much.
What kept me kind of fixated on this is that it felt like it really almost works the way I wanted it to, so finally I bit the bullet and realized I can just partially rewrite the entire logging system myself, copying what I like and not copying what I don't. I hope it is slightly clear what I'm trying to achieve, I still have some work cut out for things like factories, and properly writing some docs for this, but I wanted to already push this and perhaps get an opinion or two? |
|
Overall the newest commit looks very nice. I already had one comment on the code, see above, that one could reduce overhead also by having a wrapper for the algorithm state, is that without that wrapper no logging functions are ever called. This is what I do with the |
|
Hmmm, I would really like to avoid having to do additional wrapping, since the wrapping is already happening in the scoped value. julia> @benchmark solve($problem, $algorithm1)
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
Range (min … max): 128.583 μs … 6.155 ms ┊ GC (min … max): 0.00% … 96.95%
Time (median): 142.584 μs ┊ GC (median): 0.00%
Time (mean ± σ): 147.027 μs ± 97.708 μs ┊ GC (mean ± σ): 2.41% ± 4.95%
▄▇▆▃▄▄▇█▅▂
▂▄▄▃▄▄▆▆▇▅▅███████████▇▇▇▆▅▅▅▅▄▄▄▄▃▃▂▃▂▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▃
129 μs Histogram: frequency by time 175 μs <
Memory estimate: 156.30 KiB, allocs estimate: 10002.
julia> @benchmark solve($problem, $algorithm1)
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
Range (min … max): 164.042 μs … 6.906 ms ┊ GC (min … max): 0.00% … 97.27%
Time (median): 174.250 μs ┊ GC (median): 0.00%
Time (mean ± σ): 179.881 μs ± 100.043 μs ┊ GC (mean ± σ): 2.00% ± 4.83%
▁▅▇█▆▂▂▂▃▃▃
▁▁▂▃▆█████████████▇▇▆▆▇▇▆▆▅▅▄▃▃▃▃▂▃▃▂▂▂▂▂▂▂▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁ ▃
164 μs Histogram: frequency by time 207 μs <
Memory estimate: 156.30 KiB, allocs estimate: 10002.In other words, the overhead of the logging infrastructure when there is no logging happening is about 30-40 ns per iteration. I think I find this acceptable, but that's probably because of my use-cases far exceeding these values. I don't know what you think? (as a sidenote, this looks like a lot of allocations are happening, which is definitely something to investigate) |
|
Hm, my idea of the wrapper was that there is not even that code in the default case, but you might be right that this super simple newton is already an extreme case and the overhead does look ok, though we would always have case 2 in your scenario? For the allocs, maybe just how Newton is implemented there. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7 +/- ##
=======================================
Coverage ? 56.36%
=======================================
Files ? 5
Lines ? 220
Branches ? 0
=======================================
Hits ? 124
Misses ? 96
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
So, I've updated the default implementation, the overhead is now completely negligible and it looks like the compiler can union-split where it needs to. (There is some variability on the benchmarks depending on what my computer is doing at the same time I think) This clearly has less overhead, but the iteration now looks a bit more complicated, and we lose the ability to enable or disable the logging after the solving has started, which was previously actually possible in concurrent environments. julia> @benchmark solve($problem, $algorithm1)
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
Range (min … max): 134.250 μs … 7.939 ms ┊ GC (min … max): 0.00% … 97.87%
Time (median): 149.375 μs ┊ GC (median): 0.00%
Time (mean ± σ): 155.963 μs ± 123.466 μs ┊ GC (mean ± σ): 2.95% ± 4.94%
▆█▃▁▁▁▁▁
▁▁▁▁▁▁▁▁▁▂▂▂▂▄█████████▇▇▇▆▆▄▄▃▃▂▂▂▂▂▃▄▂▂▂▂▂▁▁▁▁▁▁▁▁▁▂▂▂▁▁▁▁▁ ▃
134 μs Histogram: frequency by time 178 μs <
Memory estimate: 156.30 KiB, allocs estimate: 10002.
julia> @benchmark solve($problem, $algorithm1)
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
Range (min … max): 141.750 μs … 9.444 ms ┊ GC (min … max): 0.00% … 98.15%
Time (median): 148.667 μs ┊ GC (median): 0.00%
Time (mean ± σ): 155.532 μs ± 128.421 μs ┊ GC (mean ± σ): 2.92% ± 4.94%
▃██▃ ▁▁▁▁
▁▂▅████████████▇▆▆▅▄▃▃▃▂▂▂▂▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▃▅▄▂▁▂▂▂▁▁▁▁▁▁▁▁▁▁ ▃
142 μs Histogram: frequency by time 180 μs <
Memory estimate: 156.30 KiB, allocs estimate: 10002. |
|
Oh, the argument to interactively add logs sounds like an argument for your approach. In my wrap idea one would have to stop, wrap, and restart-solve which might recompile a few things. I have not checked what |
|
also good it is no longer edit: checked the overall PR and understand the variables now, neat :) |
|
Ok, let me try and get some tests, some factories and the docs in, shoud make things more clear too |
|
The extended docs look very neat! I think for a few of the actions and stopping criteria I would also like to work on a “convenience layer”, that you can just say something like |
|
The docs are indeed very nice. I have a comment on
One could also try to interface some status report to some enums - but that is a bit tricky maybe with the or/_and_s ;) I can work on this after this PR when I tackle a stripping criterion factory (to handle a few kwargs automatically like maxiter or maxtime) and turn them into stopping criteria |
|
Nice! You have a good tendency to confuse me with function names: They go in full detail with all technical stuff necessary. I hope (aas mentioned) to do some them on a convenience layer a bit shorter / more accessible, but that would then be code/part of a tutorial before (with a bit less technical detail). Great work! |
|
The best part is that I actually realized this the moment I wrote it down, that group action has a very different meaning 🙃 I'm happy to change that though, just wanted to get everything more or less in and stable. (Also, I realized that in the current implementation, we could even have I think I would propose the following plan:
|
|
since I like the idea with the pairs Your next steps sound great! For the convergence, maybe have the following in mind that is even easier to split here (and naming of functions can surely be improved)
For the |
|
Ah, I think I'm following what you mean with the distinction now, that does make sense indeed. |
kellertuer
left a comment
There was a problem hiding this comment.
Though there is one minor remark I have for the docs, I already mark it as approved, since that is not an urgent one.
Thanks for the detailed technical docs. It helped me to see how nice the Logging ended up being and to also see a nice explanation for the stopping criteria.
Co-authored-by: Ronny Bergmann <git@ronnybergmann.net>
Very WIP and rough start of getting this on the road. I've basically spent the entire day toying around with a bunch of different strategies and approaches, based on as many different frameworks as I could find, and wasn't really happy with most of them.
I think I'm starting to settle on some parts I like, but this still needs a lot of work.
(this is not ready for review, just wanted to post some progress here to let you know that I am actually looking into it)
Some comments on what I have:
debugwithlogging, since I get confused about the meaning of the word debug, which for me means figuring out things that arent working. Happy to change that if this is too radical, just a suggestion.Some vague goals:
I've started by migrating some of the Manopt debug things. I am for now trying to avoid having to wrap many things, although I might go back on this. Having
problem, alg, statealso makes it not an obvious choice what to wrap.Notably missing from the things I've started porting is the logic for deciding when to print something. I have some vague intuition/notion that this should be handled by the logger, although I have not figured out a satisfying way to achieve this.
Also missing is the formatting parts. In order to unify logging and recording, the default implementation does not return strings, rather it produces data. I'm hoping to be able to tie unique id's to these logs, which in turn the logger could use to format into a string if it wants to print, or to push to a vector of data if it wants to record.
I am probably going to try use
LoggingExtras.jlto do some of the routing, ie having a logger that splits to both a file and an io and a recording device sounds importantThe factories probably will have to return both a logger to handle the events, as well as a set of action to generate the events.
all of this is already sounding incredibly involved, and I'm slightly scared about overengineering it to a point where it becomes virtually unusable, but for now I'm hopeful and will at least continue a little bit to see how it shakes out. Take everything with a grain of salt, it might be necessary to discard everything in the end.