@@ -2,6 +2,8 @@ module Profiler
22
33import .. Reactant
44using Sockets: Sockets
5+ using PrettyTables: PrettyTables
6+ using JSON3: JSON3
57
68"""
79 with_profiler(f, trace_output_dir::String; trace_device=true, trace_host=true, create_perfetto_link=false)
@@ -195,4 +197,160 @@ mutable struct ProfileServer
195197 end
196198end
197199
200+ function wrap_string (s; width= 20 )
201+ s_str = string (s)
202+ return join ([s_str[i: min (i + width - 1 , end )] for i in 1 : width: length (s_str)], " \n " )
203+ end
204+
205+ struct KernelStatsProfileResults
206+ data
207+ end
208+
209+ function KernelStatsProfileResults (data:: JSON3.Object )
210+ cols = data[" cols" ]
211+ rows = data[" rows" ]
212+ keys = Tuple (Symbol .(get .(cols, " id" )))
213+ table = Vector {NamedTuple} (undef, length (rows))
214+
215+ for (i, row) in enumerate (rows)
216+ vals = get .(row[" c" ], " v" )
217+ table[i] = NamedTuple {keys} (vals)
218+ end
219+
220+ return KernelStatsProfileResults (table)
221+ end
222+
223+ function Base. show (io:: IO , r:: KernelStatsProfileResults )
224+ tbl = r. data
225+
226+ println (io, " ╔════════════════╗" )
227+ println (io, " ║ Kernel Stats ║" )
228+ println (io, " ╚════════════════╝" )
229+
230+ isempty (tbl) && return nothing
231+
232+ fields = fieldnames (typeof (tbl[1 ]))
233+ wrapped = split .(wrap_string .(fields; width= 10 ), " \n " )
234+ nrows = maximum (length .(wrapped))
235+ column_labels = [[get (wrapped[j], i, " " ) for j in 1 : length (wrapped)] for i in 1 : nrows]
236+
237+ PrettyTables. pretty_table (
238+ io,
239+ tbl;
240+ line_breaks= true ,
241+ maximum_data_column_widths= 10 ,
242+ auto_wrap= true ,
243+ column_labels,
244+ )
245+ return nothing
246+ end
247+
248+ struct FrameworkStatsProfileResults
249+ data
250+ end
251+
252+ function FrameworkStatsProfileResults (data:: JSON3.Array{JSON3.Object} )
253+ results = Vector {Vector{NamedTuple}} ()
254+
255+ for table in data
256+ local_result = Vector {NamedTuple} ()
257+
258+ # Extract column information
259+ cols = table[" cols" ]
260+ col_ids = [Symbol (col[" id" ]) for col in cols]
261+
262+ # Extract rows
263+ rows = table[" rows" ]
264+
265+ # Parse each row into a NamedTuple
266+ for row in rows
267+ values = [cell[" v" ] for cell in row[" c" ]]
268+ nt = NamedTuple {Tuple(col_ids)} (Tuple (values))
269+ push! (local_result, nt)
270+ end
271+
272+ push! (results, local_result)
273+ end
274+
275+ return FrameworkStatsProfileResults (results)
276+ end
277+
278+ function Base. show (io:: IO , r:: FrameworkStatsProfileResults )
279+ println (io, " ╔══════════════════════════════╗" )
280+ println (io, " ║ FrameworkOpStatsResults ║" )
281+ println (io, " ╚══════════════════════════════╝" )
282+
283+ isempty (r. data) && return nothing
284+
285+ for tbl in r. data
286+ fields = fieldnames (typeof (tbl[1 ]))
287+ wrapped = split .(wrap_string .(fields; width= 10 ), " \n " )
288+ nrows = maximum (length .(wrapped))
289+ column_labels = [
290+ [get (wrapped[j], i, " " ) for j in 1 : length (wrapped)] for i in 1 : nrows
291+ ]
292+
293+ PrettyTables. pretty_table (
294+ io,
295+ tbl;
296+ line_breaks= true ,
297+ auto_wrap= true ,
298+ maximum_data_column_widths= 10 ,
299+ column_labels,
300+ )
301+ end
302+
303+ return nothing
304+ end
305+
306+ struct ReactantProfileResults
307+ kernel_stats:: KernelStatsProfileResults
308+ framework_stats:: FrameworkStatsProfileResults
309+ end
310+
311+ function Base. show (io:: IO , r:: ReactantProfileResults )
312+ println (io, " ╔═══════════════════════════════════════════════════════╗" )
313+ println (io, " ║ Reactant Profile Results ║" )
314+ println (io, " ╚═══════════════════════════════════════════════════════╝" )
315+ show (io, r. kernel_stats)
316+ println (io)
317+ show (io, r. framework_stats)
318+ return nothing
319+ end
320+
321+ function parse_xprof_profile_data (data)
322+ extmod = Base. get_extension (Reactant, :ReactantPythonCallExt )
323+ if extmod === nothing
324+ error (" Currently we require `PythonCall` to be loaded to parse xprof data." )
325+ end
326+ kernel_stats = KernelStatsProfileResults (
327+ JSON3. read (extmod. xspace_to_tools_data (data, " kernel_stats" ))
328+ )
329+ framework_stats = FrameworkStatsProfileResults (
330+ JSON3. read (extmod. xspace_to_tools_data (data, " framework_op_stats" ))
331+ )
332+ return ReactantProfileResults (kernel_stats, framework_stats)
333+ return nothing
334+ end
335+
336+ macro profile (ex)
337+ profile_dir = joinpath (tempdir (), " reactant_profile" )
338+ mkpath (profile_dir)
339+
340+ quote
341+ # TODO : optionally compile the code first and profile
342+
343+ Reactant. Profiler. with_profiler ($ (esc (profile_dir))) do
344+ $ (esc (ex))
345+ end
346+
347+ trace_output_dir = joinpath ($ (esc (profile_dir)), " plugins" , " profile" )
348+ date = maximum (readdir (trace_output_dir))
349+ traces_path = joinpath (trace_output_dir, date)
350+
351+ filename = first (f for f in readdir (traces_path) if endswith (f, " .xplane.pb" ))
352+ data = $ (parse_xprof_profile_data)(joinpath (traces_path, filename))
353+ end
354+ end
355+
198356end # module Profiler
0 commit comments