Skip to content

Commit 43ed001

Browse files
committed
fix #478, record resource usage info in provenance
1 parent ec43914 commit 43ed001

File tree

7 files changed

+95
-23
lines changed

7 files changed

+95
-23
lines changed

VERSIONS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ development head (in the master branch):
147147
fix #488, non-UTF-8 encoding causes poor error
148148
fix #476, external editing causes SLiMgui to get confused
149149
fix #479, CLI progress bar would be a nice option; this is now -p[rogress]
150+
fix #478, record resource usage information in the tree sequence provenance; now we also print user/sys CPU usage for -t[ime], since we now have the information
150151

151152

152153
version 4.3 (Eidos version 3.3):

core/main.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,6 @@ int main(int argc, char *argv[]) // FIXME: clang-tidy flags this with bugprone-e
554554

555555
// keep time (we do this whether or not the -time flag was passed)
556556
std::clock_t begin_cpu = std::clock();
557-
std::chrono::steady_clock::time_point begin_wall = std::chrono::steady_clock::now();
558557

559558
// keep memory usage information, if asked to
560559
size_t *mem_record = nullptr;
@@ -826,15 +825,20 @@ int main(int argc, char *argv[]) // FIXME: clang-tidy flags this with bugprone-e
826825
}
827826

828827
// end timing and print elapsed time
829-
std::clock_t end_cpu = std::clock();
830-
std::chrono::steady_clock::time_point end_wall = std::chrono::steady_clock::now();
831-
double cpu_time_secs = static_cast<double>(end_cpu - begin_cpu) / CLOCKS_PER_SEC;
832-
double wall_time_secs = std::chrono::duration<double>(end_wall - begin_wall).count();
833-
834828
if (keep_time)
835829
{
830+
std::clock_t end_cpu = std::clock();
831+
double cpu_time_secs = static_cast<double>(end_cpu - begin_cpu) / CLOCKS_PER_SEC;
832+
double user_time, sys_time;
833+
834+
Eidos_GetUserSysTime(&user_time, &sys_time);
835+
836836
SLIM_ERRSTREAM << "// ********** CPU time used: " << cpu_time_secs << std::endl;
837-
SLIM_ERRSTREAM << "// ********** Wall time used: " << wall_time_secs << std::endl;
837+
if (user_time > 0.0)
838+
SLIM_ERRSTREAM << "// ********** User CPU time: " << user_time << std::endl;
839+
if (sys_time > 0.0)
840+
SLIM_ERRSTREAM << "// ********** System CPU time: " << sys_time << std::endl;
841+
SLIM_ERRSTREAM << "// ********** Wall time used: " << Eidos_WallTimeSeconds() << std::endl;
838842
}
839843

840844
if (gEidosBenchmarkType != EidosBenchmarkType::kNone)

core/species.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6636,7 +6636,9 @@ void Species::WriteProvenanceTable(tsk_table_collection_t *p_tables, bool p_use_
66366636
try {
66376637
nlohmann::json j;
66386638

6639-
j["schema_version"] = "1.0.0";
6639+
// BCH 3/10/2024: Moving from schema version 1.0.0 to 1.1.0, which I believe shipped in tskit 0.5.9.
6640+
// This adds the optional `resources` key. See https://github.com/MesserLab/SLiM/issues/478.
6641+
j["schema_version"] = "1.1.0";
66406642

66416643
struct utsname name;
66426644
ret = uname(&name);
@@ -6740,6 +6742,23 @@ void Species::WriteProvenanceTable(tsk_table_collection_t *p_tables, bool p_use_
67406742
j["metadata"]["individuals"]["flags"]["18"]["name"] = "SLIM_TSK_INDIVIDUAL_RETAINED";
67416743
j["metadata"]["individuals"]["flags"]["18"]["description"] = "the individual was requested by the user to be retained only if its nodes continue to exist in the tree sequence";
67426744

6745+
// We save this information out only for runs at the command line. This data might not be available on
6746+
// all platforms; when it is unavailable, the key will be omitted. We always have elapsed wall time.
6747+
#ifndef SLIMGUI
6748+
double user_time, sys_time;
6749+
size_t peak_RSS = Eidos_GetPeakRSS();
6750+
6751+
Eidos_GetUserSysTime(&user_time, &sys_time);
6752+
6753+
j["resources"]["elapsed_time"] = Eidos_WallTimeSeconds();
6754+
if (user_time > 0.0)
6755+
j["resources"]["user_time"] = user_time;
6756+
if (sys_time > 0.0)
6757+
j["resources"]["sys_time"] = sys_time;
6758+
if (peak_RSS > 0)
6759+
j["resources"]["max_memory"] = peak_RSS;
6760+
#endif
6761+
67436762
if (p_use_newlines)
67446763
provenance_str = j.dump(4);
67456764
else

eidos/eidos_functions_other.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ EidosValue_SP Eidos_ExecuteFunction_citation(__attribute__((unused)) const std::
143143
}
144144

145145
// (float$)clock([string$ type = "cpu"])
146-
static std::chrono::steady_clock::time_point timebase = std::chrono::steady_clock::now(); // start at launch
147-
148146
EidosValue_SP Eidos_ExecuteFunction_clock(__attribute__((unused)) const std::vector<EidosValue_SP> &p_arguments, __attribute__((unused)) EidosInterpreter &p_interpreter)
149147
{
150148
// Note that this function ignores matrix/array attributes, and always returns a vector, by design
@@ -163,9 +161,7 @@ EidosValue_SP Eidos_ExecuteFunction_clock(__attribute__((unused)) const std::vec
163161
else if (type_name == "mono")
164162
{
165163
// monotonic clock time; this is best for measured user-perceived elapsed times
166-
std::chrono::steady_clock::time_point ts = std::chrono::steady_clock::now();
167-
std::chrono::steady_clock::duration clock_duration = ts - timebase;
168-
double seconds = std::chrono::duration<double>(clock_duration).count();
164+
double seconds = Eidos_WallTimeSeconds();
169165

170166
return EidosValue_SP(new (gEidosValuePool->AllocateChunk()) EidosValue_Float(seconds));
171167
}

eidos/eidos_globals.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ int gEidosFloatOutputPrecision = 6;
109109
int gEidosDebugIndent = 0;
110110
#endif
111111

112+
// start our wall clock duration timer at launch; this is for Eidos_WallTimeSeconds().
113+
std::chrono::steady_clock::time_point gEidos_WallTimeBegin = std::chrono::steady_clock::now();
114+
112115

113116
#pragma mark -
114117
#pragma mark Profiling support
@@ -1929,7 +1932,7 @@ void CheckLongTermBoundary()
19291932

19301933

19311934
#pragma mark -
1932-
#pragma mark Memory usage monitoring
1935+
#pragma mark Memory usage and runtime monitoring
19331936
#pragma mark -
19341937

19351938
//
@@ -2228,6 +2231,45 @@ void Eidos_CheckRSSAgainstMax(const std::string &p_message1, const std::string &
22282231
}
22292232
}
22302233

2234+
double Eidos_WallTimeSeconds(void)
2235+
{
2236+
std::chrono::steady_clock::time_point end_wall = std::chrono::steady_clock::now();
2237+
std::chrono::steady_clock::duration wall_duration = end_wall - gEidos_WallTimeBegin;
2238+
double wall_time_secs = std::chrono::duration<double>(wall_duration).count();
2239+
2240+
return wall_time_secs;
2241+
}
2242+
2243+
void Eidos_GetUserSysTime(double *p_user_time, double *p_sys_time)
2244+
{
2245+
double user_time, sys_time;
2246+
2247+
#if defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
2248+
/* BSD, Linux, and OSX -------------------------------------- */
2249+
struct rusage rusage;
2250+
getrusage( RUSAGE_SELF, &rusage );
2251+
2252+
user_time = rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec / 1000000.0;
2253+
sys_time = rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec / 1000000.0;
2254+
#elif defined(_WIN32)
2255+
/* Windows might be the same? Let's find out! -------------- */
2256+
struct rusage rusage;
2257+
getrusage( RUSAGE_SELF, &rusage );
2258+
2259+
user_time = rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec / 1000000.0;
2260+
sys_time = rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec / 1000000.0;
2261+
#else
2262+
/* Unknown OS ----------------------------------------------- */
2263+
user_time = 0.0; /* Unsupported. */
2264+
sys_time = 0.0; /* Unsupported. */
2265+
#endif
2266+
2267+
if (p_user_time)
2268+
*p_user_time = user_time;
2269+
if (p_sys_time)
2270+
*p_sys_time = sys_time;
2271+
}
2272+
22312273

22322274
#pragma mark -
22332275
#pragma mark File I/O

eidos/eidos_globals.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@ void CheckLongTermBoundary();
244244

245245
// *******************************************************************************************************************
246246
//
247-
// Memory usage monitoring
247+
// Memory usage and runtime monitoring
248248
//
249249
#pragma mark -
250-
#pragma mark Memory usage monitoring
250+
#pragma mark Memory usage and runtime monitoring
251251
#pragma mark -
252252

253253
// Memory-monitoring calls. See the .cpp for comments. These return a size in bytes.
@@ -263,6 +263,12 @@ size_t Eidos_GetMaxRSS(void);
263263
// It is a good idea to check eidos_do_memory_checks before calling this, to save calling overhead.
264264
void Eidos_CheckRSSAgainstMax(const std::string &p_message1, const std::string &p_message2);
265265

266+
// This provides the elapsed wall clock time, in seconds, since process startup.
267+
double Eidos_WallTimeSeconds(void);
268+
269+
// This provides total process time usage in terms of user CPU time and system CPU time, not wall clock time.
270+
void Eidos_GetUserSysTime(double *p_user_time, double *p_sys_time);
271+
266272

267273
// *******************************************************************************************************************
268274
//

eidostool/main.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ int main(int argc, const char * argv[])
170170

171171
// keep time (we do this whether or not the -time flag was passed)
172172
std::clock_t begin_cpu = std::clock();
173-
std::chrono::steady_clock::time_point begin_wall = std::chrono::steady_clock::now();
174173

175174
// keep memory usage information, if asked to
176175
size_t initial_mem_usage = 0;
@@ -254,15 +253,20 @@ int main(int argc, const char * argv[])
254253
Eidos_FlushFiles();
255254

256255
// end timing and print elapsed time
257-
std::clock_t end_cpu = std::clock();
258-
std::chrono::steady_clock::time_point end_wall = std::chrono::steady_clock::now();
259-
double cpu_time_secs = static_cast<double>(end_cpu - begin_cpu) / CLOCKS_PER_SEC;
260-
double wall_time_secs = std::chrono::duration<double>(end_wall - begin_wall).count();
261-
262256
if (keep_time)
263257
{
258+
std::clock_t end_cpu = std::clock();
259+
double cpu_time_secs = static_cast<double>(end_cpu - begin_cpu) / CLOCKS_PER_SEC;
260+
double user_time, sys_time;
261+
262+
Eidos_GetUserSysTime(&user_time, &sys_time);
263+
264264
std::cout << "// ********** CPU time used: " << cpu_time_secs << std::endl;
265-
std::cout << "// ********** Wall time used: " << wall_time_secs << std::endl;
265+
if (user_time > 0.0)
266+
std::cout << "// ********** User CPU time: " << user_time << std::endl;
267+
if (sys_time > 0.0)
268+
std::cout << "// ********** System CPU time: " << sys_time << std::endl;
269+
std::cout << "// ********** Wall time used: " << Eidos_WallTimeSeconds() << std::endl;
266270
}
267271

268272
// print memory usage stats

0 commit comments

Comments
 (0)