-
Notifications
You must be signed in to change notification settings - Fork 164
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
Remove VISITED_STAT #5261
base: master
Are you sure you want to change the base?
Remove VISITED_STAT #5261
Changes from all commits
70d4fd7
fea3f62
682abd3
0ffedbe
971d2c0
aaeae1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,12 +149,6 @@ | |
int profiledThread; | ||
#endif | ||
|
||
// Have we previously profiled this execution of GAP? We need this because | ||
// code coverage doesn't work more than once, as we use a bit in each Stat | ||
// to mark if we previously executed this statement, which we can't | ||
// clear | ||
UInt profiledPreviously; | ||
|
||
Int LongJmpOccurred; | ||
|
||
// We store the value of RecursionDepth each time we enter a function. | ||
|
@@ -163,6 +157,10 @@ | |
// We need to store the actual values, as RecursionDepth can increase | ||
// by more than one when a GAP function is called | ||
Obj visitedDepths; | ||
|
||
// Mark which statements in which files have been visited. | ||
// This is a plist (index by file id) of plists (indexed by line) | ||
Obj visitedStatements; | ||
} profileState; | ||
|
||
// Some GAP functionality (such as syntaxtree) evaluates expressions, which makes | ||
|
@@ -277,7 +275,8 @@ | |
return copy; | ||
} | ||
|
||
|
||
// Check if the filename of the file 'id' has even been outputted. | ||
// We only output this once per file to reduce file size. | ||
static inline void outputFilenameIdIfRequired(UInt id) | ||
{ | ||
if (id == 0) { | ||
|
@@ -345,6 +344,7 @@ | |
HashUnlock(&profileState); | ||
} | ||
|
||
// Called whenever a function is entered | ||
static void enterFunction(Obj func) | ||
{ | ||
#ifdef HPCGAP | ||
|
@@ -356,6 +356,7 @@ | |
HookedLineOutput(func, 'I'); | ||
} | ||
|
||
// Called whenever a function exits | ||
static void leaveFunction(Obj func) | ||
{ | ||
#ifdef HPCGAP | ||
|
@@ -379,6 +380,10 @@ | |
** If we could rely on the existence of the IO package, we would use that here. | ||
** however, we want to be able to start compressing files right at the start | ||
** of GAP's execution, before anything else is done. | ||
** | ||
** This has not switched to using GAP's internal gzip support because by | ||
** using an external gzip we get free parallelisation, and GAP code is not | ||
** 'charged' for the time taken to compress the profile output. | ||
*/ | ||
|
||
static BOOL endsWithgz(const char * s) | ||
|
@@ -532,9 +537,35 @@ | |
} | ||
} | ||
|
||
// Mark line as visited, and return true if the line has been previously | ||
// visited (executed) | ||
static BOOL markVisited(int fileid, UInt line) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is not thread safe, yet is not called from a critical section either. Can't that cause problems in HPC-GAP? So perhaps it needs a lock? Or perhaps we can make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (old reply!) We only call this from one thread -- we lock profiling to a single thread (you can see this from the caller). There could be issues to do with ownership of the datastructures here in HPC-GAP, if they are created in one thread and used in another. I'm not opposed to a fix for that, but it wouldn't shock me if various things don't work in HPC-GAP with profiling. |
||
{ | ||
// Some STATs end up without a file or line -- do not output these | ||
// as they would just confuse the profile generation later. | ||
if (fileid == 0 || line == 0) { | ||
return TRUE; | ||
} | ||
|
||
if (LEN_PLIST(profileState.visitedStatements) < fileid || | ||
!ELM_PLIST(profileState.visitedStatements, fileid)) { | ||
AssPlist(profileState.visitedStatements, fileid, | ||
NEW_PLIST(T_PLIST, 0)); | ||
} | ||
|
||
Obj linelist = ELM_PLIST(profileState.visitedStatements, fileid); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess executing all this for every single file is quite excessive. A possible optimization: we do something like for
Or one could go one step further and store There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I have no idea if either of these would be worthwhile, but then again, this is executed for every single expression or statement, so we should measure and make it as fast as possible? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. markedVisited is only about 2% of the time taken in profiling (that's less than I expected, but it's what cachegrind reports). Almost all the time is spent in printing. |
||
|
||
if (LEN_PLIST(linelist) < line || !ELM_PLIST(linelist, line)) { | ||
AssPlist(linelist, line, True); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we can use a blist instead of a plist to reduce memory usage by a factor of 64 or so? I think the main missing ingredient would be a function which takes a blist and efficiently increases its size by adding zero blocks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh and perhaps another helper which takes a blist and an index and returns There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you say, I started trying to use blists then discovered there is no way to extend a blist while having it remain a blist. It probably wouldn't be a bad idea to add such extensions to blists (maybe even accessible from the GAP level), but that was a rabbit-hole I didn't want to fall down, as it could be done later / more generally (should we just add a generic "extend list, and use this member to fill the new values, and special case that for blists?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't try to make something clever or generic. Thinking again about it, I would indeed just aim for a purely kernel internal API which does exactly what we need. These could even be private inside int SAFE_TEST_BIT_BLIST(Obj list, UInt pos)
{
if (pos > LEN_BLIST(list)
return 0;
return TEST_BIT_BLIST(list, pos);
}
void SAFE_SET_BIT_BLIST(Obj list, UInt pos)
{
if (SIZE_OBJ(list) < SIZE_PLEN_BLIST(pos))
ResizeBag(list, SIZE_PLEN_BLIST(pos));
SET_BIT_BLIST(list, pos);
//CLEAR_FILTS_LIST(list); // not really necessary for our application
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have a quick look. I worry about not testing such functions (you didn't do SET_LEN_BLIST in SAFE_SET_BIT_BLIST, which I think is required), I will take a look at doing this with BLists, however. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh sure. I'm not even sure that code compiles, I just copy&pasted it quickly. Yeah, it should have something like |
||
return FALSE; | ||
} | ||
return TRUE; | ||
} | ||
|
||
// type : the type of the statement | ||
// exec : are we executing this statement | ||
// visit: Was this statement previously visited (that is, executed) | ||
// This deals with code which has been compiled into bytecode. | ||
static inline void | ||
outputStat(Int fileid, int line, int type, BOOL exec, BOOL visited) | ||
{ | ||
|
@@ -562,6 +593,10 @@ | |
printOutput(fileid, line, exec, visited); | ||
} | ||
|
||
// This deals with code which is interpreted without being turned into | ||
// bytecode. This is only code which is outside of functions and loops. | ||
// GAP reports the file, line, and if the code is executed or skipped (exec). | ||
// Code is skipped when an 'if' statement is false, for example. | ||
static inline void outputInterpretedStat(int fileid, int line, BOOL exec) | ||
{ | ||
CheckLeaveFunctionsAfterLongjmp(); | ||
|
@@ -588,16 +623,14 @@ | |
return; | ||
#endif | ||
|
||
BOOL visited = VISITED_STAT(stat); | ||
int fileid = getFilenameIdOfCurrentFunction(); | ||
int line = LINE_STAT(stat); | ||
|
||
if (!visited) { | ||
SET_VISITED_STAT(stat); | ||
} | ||
BOOL visited = markVisited(fileid, line); | ||
|
||
if (profileState.OutputRepeats || !visited) { | ||
HashLock(&profileState); | ||
outputStat(getFilenameIdOfCurrentFunction(), LINE_STAT(stat), | ||
TNUM_STAT(stat), TRUE, visited); | ||
outputStat(fileid, line, TNUM_STAT(stat), TRUE, visited); | ||
HashUnlock(&profileState); | ||
} | ||
} | ||
|
@@ -671,7 +704,6 @@ | |
|
||
profileState.status = Profile_Active; | ||
RegisterThrowObserver(ProfileRegisterLongJmpOccurred); | ||
profileState.profiledPreviously = 1; | ||
#ifdef HPCGAP | ||
profileState.profiledThread = TLS(threadID); | ||
#endif | ||
|
@@ -725,16 +757,11 @@ | |
return Fail; | ||
} | ||
|
||
if(profileState.profiledPreviously && | ||
coverage == True) { | ||
ErrorMayQuit("Code coverage can only be started once per" | ||
" GAP session. Please exit GAP and restart. Sorry.",0,0); | ||
} | ||
|
||
memset(&profileState, 0, sizeof(profileState)); | ||
|
||
OutputtedFilenameList = NEW_PLIST(T_PLIST, 0); | ||
profileState.visitedDepths = NEW_PLIST(T_PLIST, 0); | ||
profileState.visitedStatements = NEW_PLIST(T_PLIST, 0); | ||
|
||
RequireStringRep(SELF_NAME, filename); | ||
|
||
|
@@ -798,7 +825,6 @@ | |
|
||
profileState.status = Profile_Active; | ||
RegisterThrowObserver(ProfileRegisterLongJmpOccurred); | ||
profileState.profiledPreviously = 1; | ||
#ifdef HPCGAP | ||
profileState.profiledThread = TLS(threadID); | ||
#endif | ||
|
@@ -875,6 +901,8 @@ | |
InitGVarFuncsFromTable( GVarFuncs ); | ||
|
||
profileState.visitedDepths = NEW_PLIST(T_PLIST, 0); | ||
profileState.visitedStatements = NEW_PLIST(T_PLIST, 0); | ||
|
||
OutputtedFilenameList = NEW_PLIST(T_PLIST, 0); | ||
return 0; | ||
} | ||
|
@@ -889,6 +917,8 @@ | |
InitHdlrFuncsFromTable( GVarFuncs ); | ||
InitGlobalBag(&OutputtedFilenameList, "src/profile.c:OutputtedFileList"); | ||
InitGlobalBag(&profileState.visitedDepths, "src/profile.c:visitedDepths"); | ||
InitGlobalBag(&profileState.visitedStatements, | ||
"src/profile.c:visitedStatements"); | ||
return 0; | ||
} | ||
|
||
|
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.
Might as well put that bit to use again (and possibly allow generation of slightly nicer code?)