-
Notifications
You must be signed in to change notification settings - Fork 161
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
Add support for profiling interpreted code #2772
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2772 +/- ##
===========================================
+ Coverage 76.1% 83.65% +7.55%
===========================================
Files 480 686 +206
Lines 240982 350857 +109875
===========================================
+ Hits 183391 293523 +110132
+ Misses 57591 57334 -257
|
Hmm.. this is hitting some lines it shouldn't. Investigating. |
bbb6988
to
d06176a
Compare
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 looks sensible and useful overall!
But I am a bit surprised about the huge increase in coverage reported by codecov. Unfortunately, their PR specific pages like https://codecov.io/gh/gap-system/gap/pull/2772/ have been timing out for the past 1-2 weeks (and my support emails were answered with generic "we are looking into it, thank you for your patience" replies). So I can't see whether this is legitimate or a fluke w/o testing this manually right now.
So, not that I am mistrusting this code, but perhaps @ChrisJefferson could just confirm whether + 6.7% in coverage is legit?
src/io.c
Outdated
@@ -59,6 +59,9 @@ | |||
** | |||
** 'number' is the number of the current line, is used in error messages. | |||
** | |||
** 'numberInterpreter' is the number of the line where the fragment of code | |||
** currently being interpreted started, is used for profiling |
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.
change the comma to a semicolon or period? I was at first slightly confused by reading it like 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.
Also, the corresponding GAPState member is named InterpreterStartLine
-- why not name the two variables similarly? I find InterpreterStartLine
much clearer than numberInterpreter
.
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 named it numberInterpreter to line up with number (the previous variable), but I agree that's a silly name for a variable, and I shouldn't be carrying on it's usage :)
return; | ||
} | ||
// Catch the case we arrive here and profiling is already disabled | ||
if (!profileState_Active) { |
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.
Off-topic: profileState_Active
is not used outside this file anymore, as far as I can tell, so it could be moved back into struct ProfileState
, couldn't it? (I am not suggesting you should do anything about this in this PR, just wondering while I am reading through this code again).
Also, there is no real reason why fopenMaybeCompressed
and fcloseMaybeCompressed
take struct ProfileState* ps
as argument, instead of just accessing the profileState
global directly, is there?
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 taking as an argument was beginning progress towards allowing multiple profilestates, so one could profile multiple threads in HPC-GAP, but it was never remotely finished. Should have a cleanup in this area, I agree.
// Statement not attached to a file | ||
if (nameid == 0) { | ||
return; | ||
} |
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.
Wouldn't it make more sense to check nameid==0
before calling outputFilenameIdIfRequired
?
Ah, OK, I see it was like that before (and outputFilenameIdIfRequired
does nothing if nameid is zero). Ah well,
The +6.7 is legit, the reason for such high coverage becomes obvious if I cut+paste the longest files. None of these were covered before, as they are all executed in the interpreter. Things like conwdat3.g used to have 0 lines of code.
|
After further inspection, I've found a couple of places where coverage has got worse (mainly to do with lines containing things like a single |
Ahh, I see, thanks for the explanation. I agree that this should wait for GAP 4.11 Dually to this PR, what we are still lacking is a way to find unused .g / .gd / .gi files which have zero coverage because they are never read. |
BTW, while browsing Codecov PR pages is still broken, one can inspect the commit at codecov to at least be able to browse through files and get their coverage. I.e.: Then one can use good old eyeballs to visually "diff" this against master. (Note that https://codecov.io/gh/gap-system/gap also shows outdated information -- I sent a bugreport to Codecov about that earlier. Apparently, they don't show coverage the HEAD commit of master there, but rather they use the commit date to pick the commit/report to show... And as it happens, the last two PRs we "merged" via rebasing had older commit dates than the one before...) |
Hi @ChrisJefferson! This is cool. With the Semigroups and Digraphs packages, we use the profiling stuff in Travis to check the code coverage of our packages (we make sure that the coverage never falls below a certain level). When I test with this PR, a lot of the files have decreased coverage, to the point that Travis fails. This seems to be solely due to more missed lines, including lines containing a single else, as you mention above, but there are also missed lines that include things like: A temporary solution would be to reduce our required coverage for now, but it's not the nicest solution. I can give you more specific details if you'd like. |
@ChrisJefferson FYI, |
@@ -195,6 +195,10 @@ void Match ( | |||
{ | |||
Char errmsg [256]; | |||
|
|||
if (STATE(InterpreterStartLine) == 0) { | |||
STATE(InterpreterStartLine) = STATE(SymbolStartLine); |
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.
BTW, InterpreterStartLine
might be incorrect if there is a recursive interpreter call (right at its start, resp. right after it ended). So perhaps InterpreterStartLine
should be stored and restored along with Symbol and other interpreter state. Unfortunately, that whole business of storing interpreter state is something I have not yet had time to clean up and refactor (though it is on my implicit TODO); as a result, Symbol
is (re)stored via IO()->Input->symbol
in io.c
; while StackObj
is (re)stored via PushPlist(STATE(IntrState), STATE(StackObj));
in intrprtr.c
; and other bits and pieces are (re)stored via various code in read.c
.
Anyway, that's hypothetical, I don't know whether this really manifests as a problem.
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 am saving/restoring in io.c, in the same place as Symbol is. I started thinking about trying to neaten this up, then got scared away, congratulations on your progress in cleaning up this area, it's very hairy :)
Hi @wilfwilson , I believe I've cleared up those random elses, could you try again and see if things are improved? Thanks for the test case. There is now only one problem I know about, which is (I believe) unfixable, but I will mention. Consider the following snippet, from algfp.gi:
In current master, line 385 is marked as "not executed", and everything else is not marked at all. With this PR, all lines are marked as executed. That is because the closing bracket of line 385 is considered to "execute" the InstallMethod call, which makes line 385 marked as executed. Note that if that closing bracket is moved to a new line, then the function on line 385 is correctly marked as not executed. Of course a future token-based profiler could handle these issues of multiple statements on a line. |
Hi @ChrisJefferson thanks for your changes. I haven't looked at everything in super close detail, but where I have looked I haven't seen any weird behaviour, and moreover, this does not seem to decrease the Semigroups package code coverage in the way that the previous version of this PR did. So: I'm happy for this to be merged whenever. |
Actually, that last caveat, despite making perfect sense, also means that I now have serious reservations about this PR: There are tons of 1-line method installations like this in the kernel, and now we'd not be able anymore to tell if they get coverage or not... But I am not quite willing to give up hope for that yet... I mean, sure, there is code like this, where we'll never be able to resolve this unless we stored profiling data token-based: InstallMethod(NestingDepthM, [IsGF2MatrixRep], m->2); But in the example we quote, couldn't we hack As to tokenization: I am not sure whether any profiling visualization tools support displaying data on a finer than per-line level. It sure would be cool if they did, though. |
So, with your suggestion it's easy to fix:
Which is a fairly common pattern. The problem if it's all on one line is of course you parse and "execute" lots, include a list construction, grabbing a couple of identifiers, that kind of thing. |
23e2043
to
0e37351
Compare
src/modules.h
Outdated
@@ -40,7 +40,7 @@ | |||
** | |||
*/ | |||
|
|||
#define GAP_KERNEL_MAJOR_VERSION 3 | |||
#define GAP_KERNEL_MAJOR_VERSION 4 |
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 needs to be incremented again, as the T_COPYING PR also increment this.
Increments GAP_KERNEL_MAJOR_VERSION for packages which use profiling hooks
0e37351
to
d9ae3d9
Compare
d9ae3d9
to
353883c
Compare
I think this is now clean. I am in two minds about the whole "sneak something extra into |
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.
Looks good to me.
I noticed after a recent PR of Max's, it is now easy to add profiling of interpreted code.
This PR does that. I have benchmarked carefully and can't measure any more overhead -- this isn't surprising as the interpreter runs very little code (any function or loop is "coded", and then run as bytecode).
I export KERNEL_API_VERSION, because in the profiling package and debugging package I need to know if this PR happened. In profiling it just breaks testing, in debugging it breaks the package but I will release an updated version.
It might be the case we get some strange coverage out, I'm partly going to see what this PR produces, and compare with previous GAP.