function [varargout] = runWithProfiling(funcHandle, nOutputs, saveDirectory) %RUNWITHPROFILING Profile runtime and memory usage, and log stats to JSON file. % % Usage: % [a, b, c, stats] = runWithProfiling(@() sim.run(), 3, './Results/'); % % Inputs: % funcHandle - Function handle to run (e.g., @() sim.run()) % nOutputs - Number of outputs expected from funcHandle % saveDirectory - Directory path to save stats JSON file % % Outputs: % varargout - Outputs of funcHandle (1 to nOutputs) % - Final output is a struct 'stats' with fields: % .runtime - Wall-clock time (s) % .workspaceMemoryMB - Total memory used (MB) % .timestamp - ISO timestamp of run % .hostname - System hostname % Time the function tStart = tic; [varargout{1:nOutputs}] = funcHandle(); runtime = toc(tStart); % Estimate workspace memory usage try vars = whos; totalBytes = sum([vars.bytes]); workspaceMemoryMB = totalBytes / 1e6; catch workspaceMemoryMB = NaN; end % Create stats struct stats.runtime = runtime; stats.workspaceMemoryMB = workspaceMemoryMB; stats.timestamp = datestr(now, 'yyyy-mm-dd_HH-MM-SS'); [~, hostname] = system('hostname'); stats.hostname = strtrim(hostname); % Save as JSON try if ~exist(saveDirectory, 'dir') mkdir(saveDirectory); end filename = fullfile(saveDirectory, ['profiling_stats_', stats.timestamp, '.json']); jsonStr = jsonencode(stats, 'PrettyPrint', true); fid = fopen(filename, 'w'); fwrite(fid, jsonStr, 'char'); fclose(fid); catch ME warning('Failed to save profiling stats: %s', ME.message); end % Return stats varargout{nOutputs + 1} = stats; end