UNPKG

@specs-feup/clava

Version:

A C/C++ source-to-source compiler written in Typescript

315 lines (264 loc) 9.88 kB
import Platforms from "@specs-feup/lara/api/lara/Platforms.js"; import TimerBase from "@specs-feup/lara/api/lara/code/TimerBase.js"; import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; import { TimerUnit } from "@specs-feup/lara/api/lara/util/TimeUnits.js"; import { FileJp, Joinpoint, Scope } from "../../Joinpoints.js"; import Clava from "../../clava/Clava.js"; import ClavaJoinPoints from "../../clava/ClavaJoinPoints.js"; import Logger from "./Logger.js"; export default class Timer extends TimerBase<Joinpoint> { addedDefines: Set<string> = new Set<string>(); /** * Times the code of a given section. * * @param $start - Starting point of the time measure * @param prefix - Message that will appear before the time measure. If undefined, empty string will be used. * @param $end - Ending point of the time measure. If undefined, measure is done around starting point. */ time($start: Joinpoint, prefix: string = "", $end: Joinpoint = $start) { if (!this._timeValidate($start, $end, "function")) { return; } const $file = $start.getAncestor("file") as FileJp | undefined; if ($file === undefined) { console.log( "Could not find the corresponding file of the given joinpoint: " + $start.joinPointType ); return; } else if ($file.isCxx) { return this._time_cpp($start, prefix, $end); } else { return this._time_c($start, prefix, $end); } } _time_cpp($start: Joinpoint, prefix: string = "", $end: Joinpoint = $start) { if (this.timeUnits.unit == TimerUnit.DAYS) { throw "Timer Exception: Timer metrics not implemented for DAYS in C++"; } const cppUnit = this.timeUnits.getCppTimeUnit()!; const logger = new Logger(false, this.filename); const $file = $start.getAncestor("file") as FileJp; // Add include $file.addInclude("chrono", true); const startVar = IdGenerator.next("clava_timing_start_"); const endVar = IdGenerator.next("clava_timing_end_"); const $codeTic = ClavaJoinPoints.stmtLiteral(this._timer_cpp_now(startVar)); const $codeToc = ClavaJoinPoints.stmtLiteral(this._timer_cpp_now(endVar)); const $insertionTic = $codeTic; const $insertionToc = $codeToc; // Declare variable for time interval, which uses calculation as initialization const timeIntervalVar = IdGenerator.next("clava_timing_duration_"); // Create literal node with calculation of time interval const $timingResult = ClavaJoinPoints.exprLiteral( "long long " + this._timer_cpp_calc_interval( startVar, endVar, cppUnit, timeIntervalVar ) ); // Build message logger.append(prefix).appendLong(timeIntervalVar); if (this.printUnit) { logger.append(this.timeUnits.getUnitsString()); } logger.ln(); // Check if $start is a scope if ($start instanceof Scope) { $start.insertBegin($insertionTic); } else { $start.insertBefore($insertionTic); } const $startVarDecl = ClavaJoinPoints.stmtLiteral( this._timer_cpp_define_time_var(startVar) ); const $endVarDecl = ClavaJoinPoints.stmtLiteral( this._timer_cpp_define_time_var(endVar) ); $insertionTic.insertBefore($startVarDecl); $insertionTic.insertBefore($endVarDecl); let afterJp = undefined; // Check if $end is a scope if ($end instanceof Scope) { $end.insertEnd($insertionToc); } else { $end.insertAfter($insertionToc); } $codeToc.insertAfter($timingResult); afterJp = $timingResult; // Log time information if (this.print) { logger.log($timingResult); afterJp = logger.getAfterJp(); } this.setAfterJp(afterJp); return timeIntervalVar; } _time_c($start: Joinpoint, prefix: string = "", $end: Joinpoint = $start) { const logger = new Logger(false, this.filename); const $file = $start.getAncestor("file") as FileJp; let $varDecl, $codeBefore, $codeAfter, $timingResult; // Declare variable for time interval, which uses calculation as initialization const timeIntervalVar = IdGenerator.next("clava_timing_duration_"); const $timingResultDecl = ClavaJoinPoints.varDeclNoInit( timeIntervalVar, ClavaJoinPoints.builtinType("double") ); if (Platforms.isWindows()) { //use QueryPerformanceCounter // Add includes $file.addInclude("time.h", true); $file.addInclude("windows.h", true); // get variable names const startVar = IdGenerator.next("clava_timing_start_"); const endVar = IdGenerator.next("clava_timing_end_"); const frequencyVar = IdGenerator.next("clava_timing_frequency_"); $varDecl = ClavaJoinPoints.stmtLiteral( this._timer_c_windows_declare_vars(startVar, endVar, frequencyVar) ); $codeBefore = ClavaJoinPoints.stmtLiteral( this._timer_c_windows_get_time(startVar) ); $codeAfter = ClavaJoinPoints.stmtLiteral( this._timer_c_windows_get_time(endVar) ); // Create literal node with calculation of time interval $timingResult = ClavaJoinPoints.exprLiteral( this._timer_c_windows_calc_interval( startVar, endVar, timeIntervalVar, frequencyVar, String(this.timeUnits.getMagnitudeFactorFromSeconds()) ), $timingResultDecl.type ); } else if (Platforms.isLinux()) { // Add includes $file.addInclude("time.h", true); // If C99 or C11 standard, needs define at the beginning of the file // https://stackoverflow.com/questions/42597685/storage-size-of-timespec-isnt-known const needsDefine = Clava.getStandard() === "c99" || Clava.getStandard() === "c11"; if (needsDefine && !this.addedDefines.has($file.location)) { $file.insertBegin("#define _POSIX_C_SOURCE 199309L"); this.addedDefines.add($file.location); } // get variable names const startVar = IdGenerator.next("clava_timing_start_"); const endVar = IdGenerator.next("clava_timing_end_"); $varDecl = ClavaJoinPoints.stmtLiteral( this._timer_c_linux_declare_vars(startVar, endVar) ); $codeBefore = ClavaJoinPoints.stmtLiteral( this._timer_c_linux_get_time(startVar) ); $codeAfter = ClavaJoinPoints.stmtLiteral( this._timer_c_linux_get_time(endVar) ); // Create literal node with calculation of time interval $timingResult = ClavaJoinPoints.exprLiteral( this._timer_c_linux_calc_interval( startVar, endVar, timeIntervalVar, String(this.timeUnits.getMagnitudeFactorFromSeconds()) ), $timingResultDecl.type ); } else { throw new Error("Timer Exception: Platform not supported (Windows and Linux only)"); } // Build message logger.append(prefix).appendDouble(timeIntervalVar); if (this.printUnit) { logger.append(this.timeUnits.getUnitsString()); } logger.ln(); const $insertionTic = $codeBefore; const $insertionToc = $codeAfter; // Check if $start is a scope if ($start instanceof Scope) { // Insert code $start.insertBegin($insertionTic); } else { // Insert code $start.insertBefore($insertionTic); } $insertionTic.insertBefore($varDecl); $insertionTic.insertBefore($timingResultDecl); let afterJp = undefined; // Check if $end is a scope if ($end instanceof Scope) { $end.insertEnd($insertionToc); } else { $end.insertAfter($insertionToc); } afterJp = $codeAfter.insertAfter($timingResult); afterJp = $timingResult; // Log time information if (this.print) { logger.log(afterJp); afterJp = logger.getAfterJp(); } this.setAfterJp(afterJp); return timeIntervalVar; } //C codedefs // Windows private _timer_c_windows_declare_vars( timeStartVar: string, timeEndVar: string, timeFrequencyVar: string ) { return `LARGE_INTEGER ${timeStartVar}, ${timeEndVar}, ${timeFrequencyVar}; QueryPerformanceFrequency(&${timeFrequencyVar});`; } private _timer_c_windows_get_time(timeVar: string) { return `QueryPerformanceCounter(&${timeVar});`; } private _timer_c_windows_calc_interval( timeStartVar: string, timeEndVar: string, timeDiffenceVar: string, timeFrequencyVar: string, factorConversion: string ) { return `${timeDiffenceVar} = ((${timeEndVar}.QuadPart - ${timeStartVar}.QuadPart) / (double)${timeFrequencyVar}.QuadPart) * (${factorConversion})`; } //Linux private _timer_c_linux_declare_vars( timeStartVar: string, timeEndVar: string ) { return `struct timespec ${timeStartVar}, ${timeEndVar};`; } private _timer_c_linux_get_time(timeVar: string) { return `clock_gettime(CLOCK_MONOTONIC, &${timeVar});`; } private _timer_c_linux_calc_interval( timeStartVar: string, timeEndVar: string, timeDiffenceVar: string, factorConversion: string ) { return `${timeDiffenceVar} = ((${timeEndVar}.tv_sec + ((double) ${timeEndVar}.tv_nsec / 1000000000)) - (${timeStartVar}.tv_sec + ((double) ${timeStartVar}.tv_nsec / 1000000000))) * (${factorConversion})`; } //Cpp codedefs private _timer_cpp_define_time_var(timeVar: string) { return `std::chrono::high_resolution_clock::time_point ${timeVar};`; } private _timer_cpp_now(timeVar: string) { return `${timeVar} = std::chrono::high_resolution_clock::now();`; } private _timer_cpp_calc_interval( startVar: string, endVar: string, unit: string, differentialVar: string ) { return `${differentialVar} = std::chrono::duration_cast<std::chrono::${unit}>(${endVar} - ${startVar}).count()`; } }