From 9c4bedab7f1e692179a81fec79366555653fe62b Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Fri, 25 Feb 2022 15:20:37 +0200 Subject: [PATCH 01/12] Started --track-energy feature implementation --- pyperf/_runner.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pyperf/_runner.py b/pyperf/_runner.py index b70e3e52..3b8a6463 100644 --- a/pyperf/_runner.py +++ b/pyperf/_runner.py @@ -104,9 +104,6 @@ def __init__(self, values=None, warmups=None, processes=None, # Set used to check that benchmark names are unique self._bench_names = set() - # result of argparser.parse_args() - self.args = None - # callback used to prepare command line arguments to spawn a worker # child process. The callback is called with prepare(runner.args, cmd). # args must be modified in-place. @@ -221,6 +218,9 @@ def __init__(self, values=None, warmups=None, processes=None, help='option used with --compare-to to name ' 'PYTHON as CHANGED_NAME ' 'and REF_PYTHON as REF_NAME in results') + parser.add_argument("--track-energy", + dest="track_energy", action="store_true", default=False, + help="Measure energy instead of wall clock time.") memory = parser.add_mutually_exclusive_group() memory.add_argument('--tracemalloc', action="store_true", @@ -230,6 +230,9 @@ def __init__(self, values=None, warmups=None, processes=None, self.argparser = parser + # result of argparser.parse_args() + self.args = self.parse_args() + def _multiline_output(self): return self.args.verbose or multiline_output(self.args) From 16956f610d1bef80c283168ba7ae9ddca897d741 Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Tue, 1 Mar 2022 18:21:46 +0200 Subject: [PATCH 02/12] Energy readings in progress --- pyperf/_runner.py | 5 +++-- pyperf/_worker.py | 21 +++++++++++++++++++-- pyperf/energy.txt | 1 + pyperf/libreaden.so | Bin 0 -> 16576 bytes 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 pyperf/energy.txt create mode 100755 pyperf/libreaden.so diff --git a/pyperf/_runner.py b/pyperf/_runner.py index 3b8a6463..4840afc5 100644 --- a/pyperf/_runner.py +++ b/pyperf/_runner.py @@ -219,7 +219,7 @@ def __init__(self, values=None, warmups=None, processes=None, 'PYTHON as CHANGED_NAME ' 'and REF_PYTHON as REF_NAME in results') parser.add_argument("--track-energy", - dest="track_energy", action="store_true", default=False, + action="store_true", default=False, help="Measure energy instead of wall clock time.") memory = parser.add_mutually_exclusive_group() @@ -231,7 +231,7 @@ def __init__(self, values=None, warmups=None, processes=None, self.argparser = parser # result of argparser.parse_args() - self.args = self.parse_args() + self.args = None def _multiline_output(self): return self.args.verbose or multiline_output(self.args) @@ -494,6 +494,7 @@ def task_func(task, loops): dt = local_timer() - t0 return dt + task = WorkerProcessTask(self, name, task_func, metadata) task.inner_loops = inner_loops diff --git a/pyperf/_worker.py b/pyperf/_worker.py index 2950aa3a..78c16f5e 100644 --- a/pyperf/_worker.py +++ b/pyperf/_worker.py @@ -15,6 +15,10 @@ MAX_WARMUP_VALUES = 300 WARMUP_SAMPLE_SIZE = 20 +# To invoke C in the context of --track-energy. +import ctypes +import pathlib + class WorkerTask: def __init__(self, runner, name, task_func, func_metadata): @@ -63,9 +67,22 @@ def _compute_values(self, values, nvalue, while True: if index > nvalue: break - - raw_value = self.task_func(self, self.loops) + + if self.args.track_energy: + print("Moment of truth begins . . .") + libname = pathlib.Path().absolute() / "libreaden.so" + c_lib = ctypes.CDLL(libname) + print("Lib found and parsed!") + e_0 = c_lib.readen(ctypes.c_char_p("energy.txt".encode('utf-8'))) + print("First energy reading retrieved:") + print(e_0) + self.task_func(self, self.loops) + e_1 = c_lib.readen(ctypes.c_char_p("energy.txt".encode('utf-8'))) + 1 + raw_value = float(e_1) - float(e_0) + else: + raw_value = self.task_func(self, self.loops) raw_value = float(raw_value) + print("Moment of truth ended!") value = raw_value / (self.loops * inner_loops) if not value and not calibrate_loops: diff --git a/pyperf/energy.txt b/pyperf/energy.txt new file mode 100644 index 00000000..a8fa06e1 --- /dev/null +++ b/pyperf/energy.txt @@ -0,0 +1 @@ +62 diff --git a/pyperf/libreaden.so b/pyperf/libreaden.so new file mode 100755 index 0000000000000000000000000000000000000000..f6b4d37b5ecae7e1eef5d7ed920fdbe7850004c6 GIT binary patch literal 16576 zcmeHOZEO_B86Nv=Fad0bLTjK2TZ$!;hK>3DXbB7E%#vW@0+#5Pma}i|JLP=m-0cBI zqIC+5V!230YNZP0N2?NvRu!e~kD&Z$jc9=WNJ@T0BC6ClNu!+Ak2tQVOx1FI-<^5Z zyX(8CYNh_DcBH#!-sk<8*>~sGGv1kR#1C}T*3<};n0P|4WIQMtLn(~yszP9hZDJvP zKP(<{ZR=)Lb82_yNeU>MrOa*Ab5+SzG2;m=YcNABk(6)%jMvIBM2Fv;d;LFOr|SwaVh-0k`>(YOmUTm{R!!D$&g+$KWxu?_jr}^o(;~ z)G%@NOO?#}wQo;9`Q@<>&i>^09izW^_Nu;d?89MFv!7T@AC5cQ|K5kC$5tHNEN=n3eWx3=o2odiKrC+t5x`H6~3nm&-Y!UPq=gtu{unW z+)bY)qEXya&yV}Cv8?`qT-GW$iM(T3!rFhN(@NTTyFXoU?EI0=_Dn8oA4&9NY^kfP zv3gG?tiE(Mkx8GjMcz&%?X2j_4br!LGVO@Ld^+p&iGJJ3q_ei@>&@f}wkSAxCzla@ zd7FZB7kZzudQ;C>eTj5NWYRsodLgH85&I78-_vew&^LLt4fx!}2j-}{uxQ;T}8fWM;nqlW7yz(bzcC3zmh z+#8gVkDndiLQMJig)k8OD4HQ_%;UGm%vyuLgXj}-R@wdmkBur(2BHi^8Hh3vWgyBxlz}J% z)iUtcricG-p7~RQIa>eL<223s*0@t!zHOfQb;EVnrM&rFqNmHP?-4Yu5^SGh&Gh6u z<#KtXXPNf3m0TpW$xqE5d?iyL#~X6a38!yJiE zm}gE;h~kc4@dzkQFgY48nIkvoGB1+pRdy}CAo<&p|FOppx_tM9d9L%0c`i=+*E#lx zj9sGGrE>8yMVw%IwTkizPxF7tftme+G@c@>+njOA0tdQTdhefZXGqaGlQcIja<9#) zFa44D-&}XcjXUnWky4si{>|s(duvwai6Bo74jcrgU05L#+E0W7M^Gr7Q0sLcy!~M z)l9QL$8Vvyc&7%Q-)S}zKg@(G9MhrCdTQT&*BEbXe!jMS@x0o*)H19hj4}{qAj&|L zfhYq}2BHi^8Hh3vWgyBxl!0&tupbfo53%>}f->~>2&%;yz07mVjM{{X&9m6&xKin{ z6kDNW?04k#8yD#B-Y@4^p5k}NRIq0-RVQWaX>3uO53%R(6{)6u>9Vva^!6wkl2BpA zxYA?2iam&*QhbYQ2>hVx2>N+R*hwdjiL zzrLYOZ`<^Uq{AJkAjfDYD)!O))x0wo`{aXs%o|5RzD{8L1o?V_aTes~3EZzBKVRTJ z2l)np@e<@02#l*BPor4nEyynvT19@5xKfc{EXFJHjox{VVEiV5-))emAex_%^_Y0j zJ0IZJ#%RZPS4I0zc>A4$_DcnR|3QA4s5Bm9;*$dVorCuDYc>3gtj9!)!1(iPV`8~D zR?!|$rCDqKz1E43%Cn5D@$;{i_P67F4D^kPA5{Cc!GA{ac%FjaN|hR4fAD*Nr{M`E4 ziv2f%-D{d)VH;5X{&6+=Qq+;BA-tTS$5XR57EJl z#DHxjivt5gVovUCEbeB^@yWNhk}Wpy0d-5Iyy2lw)c5)R8P7IIc9kxlYJlD%I# zxVv+IJF#>o$cmfF!Q6X@C^~Zj*L??`-n09F^>jza;rJ2j$nHG{;uMe@cMpvi29AdC zfsl_ox*`vpgzQ5nPG+^6aDqi1TbWh3%>)n3$TKWK6ZhatkWwdKf+Qb}2{P_A{2NDX zf?h%|3=KGm9zr@%<6;W6Y|gRu{%lbn%+qn4yfY+pkLoF=Gs$)7q;L&Vi9$-~$)PL- zlj!87=D3|Nq;uJTV396wXA6pTW1Wa_0Gr>9U5It}pw8n)gK<-e=&{FvvMwJHqe zH$YAC0L(uj(+0nQKjuL|e(&NhY8vfPyQ%g!nuDT!%)5c`hhMn;r^uh@-0;V|3fM)@ z7cM;A&Yieg5=UZ%+P3iQ}ALUF7}!Q-24s z{!(GwzM{_fJbnEBm~R5d{KizrpI6}dkU!?7K-eJ+WZ;F6|F}{BhvBFYe!w4w{4p;B zz8o4b@I%-ih5Rue24Z~+d)!CJKUMy?|3Xt5g`ZU-Ak-c4GAWpY{~AqN0w0s2_Z@Ek zRnqwVF%JiR$McjGj6cx7r3%kovHrxm9p`hy_K<x7BN*;)wA{hs}TKX8gvet%&o+ct?ul?Z?z z@E$4r{#bV$RsOIG`$Ogh2HT+j#|)`CR>hy&@|QXym{aDS3jU9JjI6-lj~IJ?(=iqB u1(kSyj`A(V_2Bu<|NlVW{r)Xmq_iv)9PPrEmrIrW*KLy)i$e|}QTz+p;ifkL literal 0 HcmV?d00001 From f765c99d22107a76f0dee81e477ae49370ad0cd7 Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Wed, 2 Mar 2022 13:02:59 +0200 Subject: [PATCH 03/12] --track-energy to be done soon --- pyperf/_manager.py | 2 ++ pyperf/_runner.py | 4 ++-- pyperf/_worker.py | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyperf/_manager.py b/pyperf/_manager.py index f29af1fa..6ff960c0 100644 --- a/pyperf/_manager.py +++ b/pyperf/_manager.py @@ -65,6 +65,8 @@ def worker_cmd(self, calibrate_loops, calibrate_warmups, wpipe): cmd.append('--tracemalloc') if args.track_memory: cmd.append('--track-memory') + if args.track_energy: + cmd.append('--track-energy') if self.runner._add_cmdline_args: self.runner._add_cmdline_args(cmd, args) diff --git a/pyperf/_runner.py b/pyperf/_runner.py index 4840afc5..64e1f9bc 100644 --- a/pyperf/_runner.py +++ b/pyperf/_runner.py @@ -219,7 +219,7 @@ def __init__(self, values=None, warmups=None, processes=None, 'PYTHON as CHANGED_NAME ' 'and REF_PYTHON as REF_NAME in results') parser.add_argument("--track-energy", - action="store_true", default=False, + action="store_true", help="Measure energy instead of wall clock time.") memory = parser.add_mutually_exclusive_group() @@ -423,7 +423,7 @@ def _main(self, task): if task.name in self._bench_names: raise ValueError("duplicated benchmark name: %r" % task.name) self._bench_names.add(task.name) - + args = self.parse_args() try: if args.worker: diff --git a/pyperf/_worker.py b/pyperf/_worker.py index 78c16f5e..c3095611 100644 --- a/pyperf/_worker.py +++ b/pyperf/_worker.py @@ -67,7 +67,6 @@ def _compute_values(self, values, nvalue, while True: if index > nvalue: break - if self.args.track_energy: print("Moment of truth begins . . .") libname = pathlib.Path().absolute() / "libreaden.so" @@ -82,7 +81,6 @@ def _compute_values(self, values, nvalue, else: raw_value = self.task_func(self, self.loops) raw_value = float(raw_value) - print("Moment of truth ended!") value = raw_value / (self.loops * inner_loops) if not value and not calibrate_loops: From 1c723f8ccfd6a8e01a30cdbb374b55a9b343b59b Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Wed, 2 Mar 2022 15:56:01 +0200 Subject: [PATCH 04/12] --track-energy complete. Port to Tegra pending --- pyperf/_worker.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pyperf/_worker.py b/pyperf/_worker.py index c3095611..0762b9b3 100644 --- a/pyperf/_worker.py +++ b/pyperf/_worker.py @@ -17,7 +17,7 @@ # To invoke C in the context of --track-energy. import ctypes -import pathlib +import os class WorkerTask: @@ -68,15 +68,13 @@ def _compute_values(self, values, nvalue, if index > nvalue: break if self.args.track_energy: - print("Moment of truth begins . . .") - libname = pathlib.Path().absolute() / "libreaden.so" - c_lib = ctypes.CDLL(libname) - print("Lib found and parsed!") - e_0 = c_lib.readen(ctypes.c_char_p("energy.txt".encode('utf-8'))) - print("First energy reading retrieved:") - print(e_0) + # Use environment variable for where the readings are stored. + c_lib = ctypes.CDLL("/home/cappadokes/code/pyperf/pyperf/libreaden.so") + # Energy value is the difference between recorded energies + # before and after executing task function. + e_0 = c_lib.readen("/home/cappadokes/code/pyperf/pyperf/energy.txt".encode('utf-8')) self.task_func(self, self.loops) - e_1 = c_lib.readen(ctypes.c_char_p("energy.txt".encode('utf-8'))) + 1 + e_1 = c_lib.readen("/home/cappadokes/code/pyperf/pyperf/energy.txt".encode('utf-8')) + 1 raw_value = float(e_1) - float(e_0) else: raw_value = self.task_func(self, self.loops) From 43afd4c6761aa24ff0c508c9fd395ec1fbac862b Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Fri, 4 Mar 2022 12:31:32 +0200 Subject: [PATCH 05/12] Added env, new lib --- pyperf/_worker.py | 11 +++++------ pyperf/libreaden.so | Bin 16576 -> 16520 bytes 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pyperf/_worker.py b/pyperf/_worker.py index 0762b9b3..7fa29d0a 100644 --- a/pyperf/_worker.py +++ b/pyperf/_worker.py @@ -69,16 +69,15 @@ def _compute_values(self, values, nvalue, break if self.args.track_energy: # Use environment variable for where the readings are stored. - c_lib = ctypes.CDLL("/home/cappadokes/code/pyperf/pyperf/libreaden.so") + c_lib = ctypes.CDLL(os.environ.get("READEN")) # Energy value is the difference between recorded energies # before and after executing task function. - e_0 = c_lib.readen("/home/cappadokes/code/pyperf/pyperf/energy.txt".encode('utf-8')) + e_0 = ctypes.c_ulonglong(c_lib.readen(os.environ.get("ENFILE").encode('utf-8'))) self.task_func(self, self.loops) - e_1 = c_lib.readen("/home/cappadokes/code/pyperf/pyperf/energy.txt".encode('utf-8')) + 1 - raw_value = float(e_1) - float(e_0) + e_1 = ctypes.c_ulonglong(c_lib.readen(os.environ.get("ENFILE").encode('utf-8'))) + raw_value = float(e_1.value) - float(e_0.value) else: - raw_value = self.task_func(self, self.loops) - raw_value = float(raw_value) + raw_value = float(self.task_func(self, self.loops)) value = raw_value / (self.loops * inner_loops) if not value and not calibrate_loops: diff --git a/pyperf/libreaden.so b/pyperf/libreaden.so index f6b4d37b5ecae7e1eef5d7ed920fdbe7850004c6..ac1c99c2d445f6b97d988cf06fcd43fd82ef7bbf 100755 GIT binary patch delta 1446 zcmZWpZ)jUp6u&qBnpcxHuWd-P)#a5kZ7byEPvdh-V^U`96CAt>g%;FVTql9B#y0UU zV$0lUiG_5B!W7YT$jG1{Izn-%bsZ}k`{LpUaYfMNgBX#~O+Oen@Of_EdsXp1&i$S9 zJLlYc?!UJ=N9X5gp_3PQvfC0G5c^V}#1(CtRa{Dm&Fd!3ST`-5_j6l%+}6^z$jQ1( z0iOQBTLjIwhu}3`366xH{qpQ;;oHoe*S>$S+BDO(^Q)7^u~Y{NMbW22&1H80XMU8v z0YS3}o90dkm^PxMV%zZPsHMG~$}UR`?J&ZRK)T`%Bze*Qc=MYUf`N32F5`Mab{12k!r- z9vp?suCb`R7z?69UbOZzIMm3x<-GNRANA9lSx&9C`exav*L1{qx*H$44{!5bGzaJS z1AQA9PjZiq%BNz_F;!mpDeI8)v5(QH|MHir){m>IdfmDVKk^E_1A#X8Q1cab8ucoA z3vzz6DlZaGo81Xjo6#nNezL1X) z(}=4uB#73^j;hW=PUv~M7kT3h(M>u~o<|Xo&oDx7U5Htf;5}8n#kgdP>x}7$(`LHv zXtI}Ug2?9Y@mBN`HBOf818d9B=l9VPJmWt`6}X7{I^6V&$o%Q|@eQjM)uaJYpidO3 z0OO*M+Tcy`JIomme1<(A^dK`7beo%qfapPvSsZCFgfqbqEy4;iPO=8y1U+8kVd_Kg zf|<0E63~!HEf5TOXaIXRycl{5N4pwwo4sTV?uCZ`m)nBbCeniSjfDTXy~fBADwMF? zxv)qpa4CF@ZUR5RRyj1lDjgpX_g=S=RfCZM53CKG``23>iTLPy@M^?E0cx0eVYmN+ z^-zgeARFnRKbPs_r>Wg|s_?$_aCt-8pfG!=N}qsP+8-UHgbyz{xrf;@^`=EAM z#6u{btWkAvb+)1hV1>4E1C z!Wqs25@(^O!O59v0@gSY4DJ%R#??SSav9`ejDs8(;&;?d>_SgT%sMt6jm3w^iAa1T z8jFyjcSc4hB4i>y9v>YcL*o(nuEg^;ORphZp$rXv*^eQt9E$ofD+ULXnrIX&<;Vis z44Zr|%Cp$(j6^XUQM67`BnJapbM2B^R5b#Y5T; ilKfbIEPVp*=DggG?4i5-$HsMUdszHFf5|I0(TrD&A^VML9nR*H+HRb8nGZD?pnC0T+2 zH;X>&7HJL^-F(jO`guRzS4UU1$W2W!J z6O2d|?5l Date: Wed, 9 Mar 2022 10:56:46 +0200 Subject: [PATCH 06/12] Added read_file.c --- pyperf/read_file.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 pyperf/read_file.c diff --git a/pyperf/read_file.c b/pyperf/read_file.c new file mode 100644 index 00000000..35a8a50f --- /dev/null +++ b/pyperf/read_file.c @@ -0,0 +1,29 @@ +#define _GNU_SOURCE +#include +#include +#include + +unsigned long long int readen(char *path) { + char *line = NULL; + size_t len = 0; + ssize_t read; + unsigned long long int data; + + FILE *fd = fopen(path, "r"); + + if (fd == NULL) + exit(EXIT_FAILURE); + + while ((read = getline(&line, &len, fd)) != -1) { + //Do nothing. + } + + data = strtoull(line, NULL, 10); + + if (line) + free(line); + + fclose(fd); + + return data; +} From 9e293c5d950399f047902870e67aaf752288e0de Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Thu, 10 Mar 2022 10:04:23 +0000 Subject: [PATCH 07/12] Remove dummy energy.txt --- pyperf/energy.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 pyperf/energy.txt diff --git a/pyperf/energy.txt b/pyperf/energy.txt deleted file mode 100644 index a8fa06e1..00000000 --- a/pyperf/energy.txt +++ /dev/null @@ -1 +0,0 @@ -62 From 6b0820e59027eec5d7f59097925f9d1f812c7622 Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Thu, 17 Mar 2022 11:25:07 +0000 Subject: [PATCH 08/12] Added unit support for energy. --- pyperf/_formatter.py | 15 +++++++++++++++ pyperf/_worker.py | 2 ++ 2 files changed, 17 insertions(+) diff --git a/pyperf/_formatter.py b/pyperf/_formatter.py index 767faf2d..2e77b871 100644 --- a/pyperf/_formatter.py +++ b/pyperf/_formatter.py @@ -39,6 +39,20 @@ def format_filesizes(sizes): return tuple(format_filesize(size) for size in sizes) +def format_energy(en): + if size < 10 * 1000: + return '%.0f uJ' % size + + if size > 10 * 1000 * 1000: + return '%.1f J' % (size / (1000.0 * 1000.0)) + + return '%.1f mJ' % (size / 1000.0) + + +def format_energies(ens): + return tuple(format_energy(en) for en in ens) + + def format_seconds(seconds): # Coarse but human readable duration if not seconds: @@ -108,6 +122,7 @@ def format_integers(numbers): 'second': format_timedeltas, 'byte': format_filesizes, 'integer': format_integers, + 'joule': format_energies, } diff --git a/pyperf/_worker.py b/pyperf/_worker.py index 7fa29d0a..1104f349 100644 --- a/pyperf/_worker.py +++ b/pyperf/_worker.py @@ -39,6 +39,8 @@ def __init__(self, runner, name, task_func, func_metadata): if 'unit' not in self.metadata: # Set default unit to seconds self.metadata['unit'] = 'second' + if args.track_energy: + self.metadata['unit'] = 'joule' self.inner_loops = None self.warmups = None From fc3fbb9493f3355c1ef278f6721b405fbff17071 Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Thu, 17 Mar 2022 13:50:23 +0000 Subject: [PATCH 09/12] Removed need for manual --inherit-environ if --track-energy is used. --- pyperf/_manager.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pyperf/_manager.py b/pyperf/_manager.py index 6ff960c0..d02c2a78 100644 --- a/pyperf/_manager.py +++ b/pyperf/_manager.py @@ -25,6 +25,27 @@ class Manager(object): def __init__(self, runner, python=None): self.runner = runner self.args = runner.args + + # If --track-energy is used, check for and + # inherit LIBREADEN, ENFILE without explicit + # input from the user. + if self.args.track_energy: + if self.args.inherit_environ is None: + self.args.inherit_environ = [] + from os import environ as curr_env + try: + lib = curr_env['LIBREADEN'] + f = curr_env['ENFILE'] + # pyperf could have been invoked by pyperformance + # and then the inheritance stuff would already be + # addressed. + if 'LIBREADEN' not in self.args.inherit_environ: + self.args.inherit_environ.append('LIBREADEN') + if 'ENFILE' not in self.args.inherit_environ: + self.args.inherit_environ.append('ENFILE') + except: + raise OSError('--track-energy needs LIBREADEN, ENFILE to function') + if python: self.python = python else: From adebc35377f9377ed8f3ded17b3288ecc89142d9 Mon Sep 17 00:00:00 2001 From: Christos Lamprakos Date: Mon, 28 Mar 2022 13:15:24 +0300 Subject: [PATCH 10/12] Added LD_LIBRARY_PATH in inherit-environ when energy tracking is on --- pyperf/_manager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyperf/_manager.py b/pyperf/_manager.py index d02c2a78..2b8a1bb4 100644 --- a/pyperf/_manager.py +++ b/pyperf/_manager.py @@ -36,6 +36,7 @@ def __init__(self, runner, python=None): try: lib = curr_env['LIBREADEN'] f = curr_env['ENFILE'] + ld = curr_env['LD_LIBRARY_PATH'] # pyperf could have been invoked by pyperformance # and then the inheritance stuff would already be # addressed. @@ -43,8 +44,10 @@ def __init__(self, runner, python=None): self.args.inherit_environ.append('LIBREADEN') if 'ENFILE' not in self.args.inherit_environ: self.args.inherit_environ.append('ENFILE') + if 'LD_LIBRARY_PATH' not in self.args.inherit_environ: + self.args.inherit_environ.append('LD_LIBRARY_PATH') except: - raise OSError('--track-energy needs LIBREADEN, ENFILE to function') + raise OSError('--track-energy needs LIBREADEN, ENFILE, LD_LIBRARY_PATH to function') if python: self.python = python From c6f860701a7a8048ef30b26d9b4478a6dfe51ab7 Mon Sep 17 00:00:00 2001 From: Christos Lamprakos Date: Tue, 29 Mar 2022 10:52:52 +0300 Subject: [PATCH 11/12] Fixed energy unit bug. --- pyperf/_formatter.py | 10 +++++----- pyperf/_manager.py | 10 +++++----- pyperf/_metadata.py | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pyperf/_formatter.py b/pyperf/_formatter.py index 2e77b871..a5b752c8 100644 --- a/pyperf/_formatter.py +++ b/pyperf/_formatter.py @@ -40,13 +40,13 @@ def format_filesizes(sizes): def format_energy(en): - if size < 10 * 1000: - return '%.0f uJ' % size + if en < 10 * 1000: + return '%.0f uJ' % en - if size > 10 * 1000 * 1000: - return '%.1f J' % (size / (1000.0 * 1000.0)) + if en > 10 * 1000 * 1000: + return '%.1f J' % (en / (1000.0 * 1000.0)) - return '%.1f mJ' % (size / 1000.0) + return '%.1f mJ' % (en / 1000.0) def format_energies(ens): diff --git a/pyperf/_manager.py b/pyperf/_manager.py index 2b8a1bb4..fe5a1695 100644 --- a/pyperf/_manager.py +++ b/pyperf/_manager.py @@ -27,27 +27,27 @@ def __init__(self, runner, python=None): self.args = runner.args # If --track-energy is used, check for and - # inherit LIBREADEN, ENFILE without explicit + # inherit READEN, ENFILE without explicit # input from the user. if self.args.track_energy: if self.args.inherit_environ is None: self.args.inherit_environ = [] from os import environ as curr_env try: - lib = curr_env['LIBREADEN'] + lib = curr_env['READEN'] f = curr_env['ENFILE'] ld = curr_env['LD_LIBRARY_PATH'] # pyperf could have been invoked by pyperformance # and then the inheritance stuff would already be # addressed. - if 'LIBREADEN' not in self.args.inherit_environ: - self.args.inherit_environ.append('LIBREADEN') + if 'READEN' not in self.args.inherit_environ: + self.args.inherit_environ.append('READEN') if 'ENFILE' not in self.args.inherit_environ: self.args.inherit_environ.append('ENFILE') if 'LD_LIBRARY_PATH' not in self.args.inherit_environ: self.args.inherit_environ.append('LD_LIBRARY_PATH') except: - raise OSError('--track-energy needs LIBREADEN, ENFILE, LD_LIBRARY_PATH to function') + raise OSError('--track-energy needs READEN, ENFILE, LD_LIBRARY_PATH to function') if python: self.python = python diff --git a/pyperf/_metadata.py b/pyperf/_metadata.py index c8cdabdc..8feb162a 100644 --- a/pyperf/_metadata.py +++ b/pyperf/_metadata.py @@ -1,6 +1,6 @@ import collections -from pyperf._formatter import (format_number, format_seconds, format_filesize, +from pyperf._formatter import (format_number, format_energy, format_seconds, format_filesize, UNIT_FORMATTERS) @@ -62,6 +62,7 @@ def format_noop(value): LOOPS = _MetadataInfo(format_number, (int,), is_strictly_positive, 'integer') WARMUPS = _MetadataInfo(format_number, (int,), is_positive, 'integer') SECONDS = _MetadataInfo(format_seconds, NUMBER_TYPES, is_positive, 'second') +JOULES = _MetadataInfo(format_energy, NUMBER_TYPES, is_positive, 'joule') # Registry of metadata keys METADATA = { From 28155076886a02923a09195da05b1cfe4325d84c Mon Sep 17 00:00:00 2001 From: "Christos P. Lamprakos" Date: Mon, 11 Apr 2022 12:48:24 +0300 Subject: [PATCH 12/12] Added --track-energy documentation --- doc/index.rst | 3 ++- doc/runner.rst | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 459a16e5..4b6c1df4 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -36,8 +36,9 @@ Features of the ``pyperf`` module: collect them. * ``--track-memory`` and ``--tracemalloc`` :ref:`options ` to track the memory usage of a benchmark. +* ``--track-energy`` :ref:`option ` to track the energy consumption of a benchmark. Based on the `Linux power capping framework `_, but simple to extend to other energy APIs. * :ref:`JSON format ` to store benchmark results. -* Support multiple units: seconds, bytes and integer. +* Support multiple units: seconds, bytes, integer and Joules. Quick Links: diff --git a/doc/runner.rst b/doc/runner.rst index 6aa480b3..648d2408 100644 --- a/doc/runner.rst +++ b/doc/runner.rst @@ -100,6 +100,7 @@ Option:: --no-locale --track-memory --tracemalloc + --track-energy * ``--python=PYTHON``: Python executable. By default, use the running Python (``sys.executable``). The Python executable must have the ``pyperf`` module @@ -150,6 +151,12 @@ Option:: ``/proc/self/smaps``. On Windows, get ``PeakPagefileUsage`` of ``GetProcessMemoryInfo()`` (of the current process): the peak value of the Commit Charge during the lifetime of this process. +* ``--track-energy``: get the energy consumption. Implementation based on the `Linux + power capping framework `_. + User needs to export 2 environment variables prior to invoking ``pyperf`` with this option; ``ENFILE``, + the absolute path to a file containing the energy consumed by the component of interest (e.g. DRAM), and + ``READEN``, the absolute path to a shared C library containing a function ``readen`` for probing the aforementioned + file. A sample implementation is provided in ``pyperf/read_file.c``. Internal usage only