-
Notifications
You must be signed in to change notification settings - Fork 162
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
Improve performance of Info, and add BindConstant #1770
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1770 +/- ##
===========================================
+ Coverage 50.42% 63.11% +12.68%
===========================================
Files 449 967 +518
Lines 234267 292951 +58684
Branches 10422 12976 +2554
===========================================
+ Hits 118120 184883 +66763
+ Misses 113370 105242 -8128
- Partials 2777 2826 +49
|
6e98b0b
to
a298dc4
Compare
Maybe change the title to mention BindConstant. Otherwise looks great to me. |
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.
Nice! A performance improvement, and the new code is (IMHO) simpler, too. I still have some remarks.
Also, this PR also seems to merge the info code for HPC-GAP back into the regular GAP library, but neither the PR nor the relevant commit mention this -- IMHO, both should.
lib/info.gi
Outdated
if IsBound(HPCGAP) then | ||
ShareInternalObj(InfoData); | ||
fi; | ||
# Give constants for the members of info class |
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.
This is a very strange sentence. Perhaps "Define constants indicating the positions of the members of the info class" ?
lib/info.gi
Outdated
## | ||
## these are all lists, and the first two should have the same length, | ||
## which defines the number of Info classes that exist | ||
## ClassNum is placed at the end so the length of the list never changes |
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 don't understand this sentence.
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'll explain it better. I don't want to put optional members at the end, because then the list of the length can change when those optional members become assigned, which doesn't work for IsAtomicPositionalObjects, which use fixed length atomic lists (that comment really is too short).
lib/info.gi
Outdated
BIND_CONSTANT("INFODATA_OUTPUT", 4); | ||
BIND_CONSTANT("INFODATA_NUM", 5); | ||
|
||
# A list of all created Info Classes |
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.
Classes -> classes
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.
Oh, I see lots of other places in this file write "Info Class" or "Info Classes". Ah well, I guess you are being consistent here then -- still seems strange, but ... shrug
lib/info.gi
Outdated
INFO_CLASSES := []; | ||
|
||
# The most recently called Info statement | ||
InfoData := rec(); |
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.
How can a record be "the most recently called Info statement"?
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 actually think that we can get rid of InfoData
but that can be done in a later PR (I can take care of it, too).
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 the meantime, perhaps say something like this:
The level and (first) selector of the most recently executed Info statement is stored in InfoData
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 will improve the comment. Note that InfoData
is used by GAPDoc. I tried hard in this commit to not break any way someone might have extended the Info
, in case anyone has.
pos := Position(InfoData.ClassNames,name); | ||
if pos <> fail then | ||
return InfoData.InfoClass(pos); | ||
atomic readwrite INFO_CLASSES do |
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.
So... are we now OK with adding atomic
back to the library, outside of if IsBound(HPCGAP)
code paths? So far, there are only two atomic
in the non-HPC GAP library, both in init.g
.
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 thought we were, now that the parser removes them in GAP. However, we should make sure we all agree about that!
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.
Well, to make sure we all agree, "somebody" has to bring it up on e.g. the mailing list...
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.
Ahh, whatever, let's just merge this in. It's deep in internal code, and one can understand the code by just ignoring the atomic
here. If somebody truly minds, I'll wait for them to bring it up on the mailing list or elsewhere.
} | ||
else { | ||
return CALL_2ARGS(InfoDecision, selectors, level); | ||
#endif |
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.
Perhaps just use ELM_LIST
here, to replace five lines by one?
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.
Because ELM_LIST
doesn't work on positional objects, or atomic positional objects.
If we make it work, then given a positional object X[3]
starts working at the GAP level, rather than the X![3]
we expect people to use.
However, some kind of unified access would be nice, as this code is fairly horrible and I wouldn't want to start writing this regularily.
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.
OK, that makes sense.
src/intrprtr.c
Outdated
if (IS_INTOBJ(index) && IS_INTOBJ(level)) { | ||
Obj res; | ||
LT_INTOBJS(res, index, level); | ||
if (res == True) { |
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.
That seems needlessly complicated -- why not replace the above three lines by if (index < level)
?
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 agree it's complicated, but I couldn't find a shorter way of comparing integers "properly". I could just use the fact that I know that <
works for a pair of INTOBJ
s.
src/intrprtr.c
Outdated
static Obj IsInfoClassListRep; | ||
|
||
Obj InfoCheckLevel(Obj selectors, Obj level) | ||
{ | ||
if (IS_POS_INTOBJ(level) && | ||
CALL_1ARGS(IsInfoClassListRep, selectors) == True) { | ||
// Fast-pass the most common failing case |
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 elsewhere we call(ed) this a "fast-path", not a "fast-pass"
src/intrprtr.c
Outdated
CALL_1ARGS(IsInfoClassListRep, selectors) == True) { | ||
// Fast-pass the most common failing case | ||
// The fast-pass never returns True because the GAP function | ||
// InfoDecision sets a number of variables as well as returning True |
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.
This explanation feels a bit backwards to me (but that may just me being dense). I'd suggest something like this:
The fast-path only deals with the case were False is returned, as in the
True case,InfoData.LastClass
andInfoData.LastLevel
need to be set.
But actually, I think we can get rid of this exception, by getting rid of InfoData
. In the meantime, though, of course it makes sense to document this exception.
bf8b49e
to
7390f5a
Compare
This is now improved, fixed all comments, and added tests, except for the slightly horrible "ifdef HPCGAP" checking inside lists bit, where we should indeed come up with something better (or I should find out how I'm supposed to read members of positional objects) |
7390f5a
to
78599de
Compare
pos := Position(InfoData.ClassNames,name); | ||
if pos <> fail then | ||
return InfoData.InfoClass(pos); | ||
atomic readwrite INFO_CLASSES do |
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.
Well, to make sure we all agree, "somebody" has to bring it up on e.g. the mailing list...
lib/global.g
Outdated
MAKE_CONSTANT_GLOBAL(name); | ||
return val; | ||
end; | ||
MAKE_READ_ONLY_GLOBAL("BIND_CONSTANT"); |
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.
Nitpick: Why not use BIND_GLOBAL
for defining BIND_CONSTANT
, i.e. BIND_GLOBAL("BIND_CONSTANT", ...)
?
lib/global.gd
Outdated
## <A>val</A>, provided it is writable, and makes it read-only. | ||
## <Ref Func="BindGlobal"/> and <Ref Func="BindConstant"/> set the global | ||
## variable named by the string <A>name</A> to the value | ||
## <A>val</A>, provided it is writable. <Ref Func="BindGlobal"/> makes |
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.
The "it" in "it is writable" is ambiguous. Perhaps "provided that variable is writable", or even "provided that variables is unbound, or bound and writable".
[Aha, but I just realize the ambiguous text was there before. sigh]
} | ||
else { | ||
return CALL_2ARGS(InfoDecision, selectors, level); | ||
#endif |
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.
OK, that makes sense.
else { | ||
return CALL_2ARGS(InfoDecision, selectors, level); | ||
#endif | ||
if (IS_INTOBJ(index) && IS_INTOBJ(level)) { |
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.
You could use ARE_INTOBJS
here. But the above is of course fine, too.
This should wait until after #1789 |
Looks like this needs a rebase and a trivial conflict resolved. Then I'd be happy to merge it -- trying to get as many performance patches in as is safe. |
lib/global.gd
Outdated
## <Ref Func="BindGlobal"/> and <Ref Func="BindConstant"/> set the global | ||
## variable named by the string <A>name</A> to the value | ||
## <A>val</A>, provided it is writable. <Ref Func="BindGlobal"/> makes | ||
## the resulting variable read-only, which <Ref Func="BindConstant"/> makes |
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 noticed: "which" -> "while"
750e32b
to
cbdf9d3
Compare
I'd be happy to see this rebased and merged. It's another useful performance improvement and makes code overall much nicer. |
This patch moves IsInfoClassRep information out of a global structure into the class, to improve performance. Also add a special case at the Kernel level to optimise the most common situation. This reduces the speed gap between Info and an equivalent if statement from 10x to ~1.5x Also remove the need for a seperate HPC-GAP implementation
cbdf9d3
to
99dd8b3
Compare
Can be merged from my POV once tests passed |
Now passing, and the bindconstant removed. |
This patch rearranges how we implement
Info
, to make it more efficient.Just to give a feeling for the times, I wrote a test which compares an
if
statement against anInfo
.f := function() local i; for i in [1..10000000] do if x = 2 then Print("!!"); fi; od; end;;
g := function() local i; for i in [1..10000000] do Info(InfoDebug, 2, "!!"); od; end;;
f
: 124msg
in 4.8: 3086ms (24x slower)g
in master: 980ms (8x slower)g
with this patch: 217ms (1.75x slower)To go any faster, I think we would have to completely tear out the current system, remove the extensibility, and possibly introduce a
TNUM
forInfo
objects, so we could avoid any error checking.EDIT: This also includes a new little function,
BindConstant
. This is likeBindGlobal
, but for Constant globals -- it just makes a quick easy way to declare constants. I experiment (I think successfully) with using it to access positional objects efficiently.