[draft] Prototype for better TLine::ExecuteEvent#21883
[draft] Prototype for better TLine::ExecuteEvent#21883linev wants to merge 2 commits intoroot-project:masterfrom
TLine::ExecuteEvent#21883Conversation
Use PaintArrowNDC in the TArrow::Paint Use attributes ModifyOn methods Shorten options handling code
Interactive objects moving require to have some temporary variables stored. Up to now many static variables were defined in TLine::ExecuteEvent which were reused by consequent methods calls. There are many disadvantages of such approach: - there is no guarantee from ROOT that always same TLine object instance called - values in static members remain and can be by mistake used by other objects - there is no chance to handle multiple gui event loops from different threads Trying to make different approach when "interactive" object preserved as transient member of the class with std::unique_ptr<>. It let solves all of mentioned problems. The only disadvantage - memory consumption for each TLine object increased by 8 bytes.
| /// Line default constructor. | ||
| /// Defined here to correctly instantiate fInteractive member | ||
|
|
||
| TLine::TLine() |
There was a problem hiding this comment.
Never for TObject-based classes
There was a problem hiding this comment.
but does it need to be defined in this class at all?
There was a problem hiding this comment.
Yes, std::unique_ptr<> need to be initialized for class which only described in CXX file
| Int_t px1, px2, py1, py2, pxold, pyold; | ||
| Double_t oldX1, oldY1, oldX2, oldY2; | ||
| Bool_t hasOld = kFALSE; | ||
| Int_t selectPoint = 0; |
There was a problem hiding this comment.
Why are hasOld and selectPoint default-initialized but the other fields aren't?
There was a problem hiding this comment.
While other depends on these two
| Bool_t opaque = parent->OpaqueMoving(); | ||
| ROOT::Internal::TLineInteractive *i; | ||
|
|
||
| auto get_interactive = [this, &i](bool force) -> bool { |
There was a problem hiding this comment.
Should be called getInteractive. Also I would rename force to createIfNull or something like that .
Although, I'm not sure this lambda should exist at all: why not simply embed the fields of TLineInteractive in TLine? (see comment above)
There was a problem hiding this comment.
why not simply embed the fields of TLineInteractive in TLine? (see comment above)
Because then each instance of TLine will get many extra fields which never used.
So one tries to decrease memory consumption by such technique
There was a problem hiding this comment.
How many such instances do we expect in the worst-case scenario? With 64 extra bytes per instance, you'd have to have more than 16k instances before you waste 1MiB of memory
There was a problem hiding this comment.
TLine is very basic object and appears everywhere. But just double or triple size just of unused data? I really do not like this.
There was a problem hiding this comment.
Sure, but, for one I'm not sure why we need to store oldX* as double (float precision should be more than enough for NDC coordinates);
also, I'm not sure I understand it correctly: do we ever need more than 1 instance of this data per thread at once? Or is this data only relevant while interacting with the line?
Because if that's the case I'd argue it should only live during the interaction itself (e.g. create a thread_local instance when interaction start, destroy it when interaction end).
There was a problem hiding this comment.
This can be different approach.
Maybe it is even better.
| if (!fInteractive && force) | ||
| fInteractive = std::make_unique<ROOT::Internal::TLineInteractive>(); | ||
| i = fInteractive.get(); | ||
| return i != nullptr; |
There was a problem hiding this comment.
Instead of setting i and returning a bool, can't this directly return i? I don't like that the captured variable gets set as a side effect: it's very hard to track (since it's not even a function out parameter). I would instead do:
if (auto lineInteractive = getInteractive(false)) {
// ...
}
or something along these lines
There was a problem hiding this comment.
By the way, i is a very bad name imho; if you want a short name since it's going to be used a lot, at least I'd call it line
There was a problem hiding this comment.
i here is for "interactive". If in general approach is fine I will change commit anyway.
if (auto lineInteractive = getInteractive(false)) {
// ...
}
This does not work good in case selectors without break.
And not very important here.
| Double_t fY1{0}; ///< Y of 1st point | ||
| Double_t fX2{0}; ///< X of 2nd point | ||
| Double_t fY2{0}; ///< Y of 2nd point | ||
| std::unique_ptr<ROOT::Internal::TLineInteractive> fInteractive; ///<! temporary object for interactivity handling |
There was a problem hiding this comment.
Is there a strong reason to make it a unique_ptr rather than embedding the fields directly? (or as a struct value)
Sure, you'd have to define the internal class in the header, and it would take up more space in the TLine instance, but is that really a problem?
There was a problem hiding this comment.
I mention - I see it as template for other classes.
There one can have big arrays used only during interactivity.
Also for TLine it is an issue - if you have 1000 of them at once.
Use of std::unique_ptr<> introduces only 8 extra bytes in size - which remain constant disregard of complexity of TLineInteractive class
|
I closing this PR - probably idea with |
Interactive objects moving require to have some temporary variables stored.
Up to now many static variables were defined in TLine::ExecuteEvent which
were reused by consequent methods calls.
There are many disadvantages of such approach:
TLineobject instance calledTrying to make different approach when "interactive" object preserved as
transient member of the class with
std::unique_ptr<>. It solves all mentionedproblems. The only disadvantage - memory consumption for each
TLineobject increased by 8 bytes.
If one agrees on using of such approach - one can adjust other ROOT classes making them
more robust and moving us forward to graphics multithreading