How to Use the Profiler to Analyze and Optimize Nelson Code Performance
Use the profile builtin to start the Nelson profiler with profile on, run your code, then call profile off followed by profile('info') or profile('show') to view execution statistics and identify bottlenecks.
The Nelson interpreter (nelson-lang/nelson) ships with a built-in profiler that records function call counts and execution times at the line level. This tool helps you pinpoint hotspots in computationally intensive scripts and measure the impact of optimizations. The profiler is implemented as a singleton Profiler class and controlled through the profile builtin command.
How the Nelson Profiler Works
The profiler architecture consists of three integrated layers that automatically capture timing data without requiring manual instrumentation of your code.
The profile Builtin Command
The entry point for users is the profile builtin, implemented in modules/profiler/builtin/cpp/profileBuiltin.cpp. This function parses subcommands (on, off, show, info, status) and forwards them to the singleton instance. When you execute profile on, the builtin calls Profiler::getInstance()->on(), which clears previous data and activates the timing hooks.
The Profiler Singleton and Timing Hooks
At the core is the Profiler singleton defined in modules/profiler/src/include/Profiler.hpp and implemented in modules/profiler/src/cpp/Profiler.cpp. This class maintains a hash map (profileMap) where keys are generated from the current call stack using Profiler::hash().
During execution, the Nelson evaluator automatically invokes:
Profiler::tic()– Returns a high-resolution timestamp when entering a function or line (lines 74–81 inProfiler.cpp).Profiler::toc(tic, stack)– Calculates elapsed nanoseconds and updates the corresponding entry inprofileMap(lines 83–105).
Data Aggregation and Reporting
When you request results via profile('info'), the profiler:
- Walks
profileMapto collapse duplicate entries - Sorts data according to the specified criterion (
SORT_BY_TOTALTIME,SORT_BY_LINE, etc.) - Returns a struct array with fields:
FunctionName,Filename,LinePosition,NumCalls,TotalTime,PerCall
The profile('show', sortOption, nbLines) command formats this data into a console table, while profsave generates a browsable HTML report.
Step-by-Step Workflow to Profile Nelson Code
Follow this sequence to accurately measure performance characteristics of your Nelson scripts.
1. Activate the Profiler
Clear any previous session and start timing:
profile on
This executes Profiler::on() which resets the internal hash map and sets the active flag.
2. Execute the Target Code
Run the functions or scripts you want to analyze. The evaluator automatically wraps each call with tic/toc pairs:
for k = 1:1000
computeIntensiveTask(k);
end
3. Stop Data Collection
Halt the profiler to freeze the statistics:
profile off
This calls Profiler::off(), stopping the timing hooks while preserving the collected data in profileMap.
4. Analyze the Results
Retrieve structured data for programmatic analysis:
p = profile('info');
disp(p(1)) % Display the hottest function
Or view a formatted table sorted by total execution time:
profile('show', 'totaltime', 10)
Available sort options include: "totaltime", "line", "percalls", "filename", "function", "nbcalls", and "nfl" (name-file-line).
5. Export for External Review
Generate an HTML report with per-file breakdowns:
p = profile('info');
profsave(p, '/tmp/myProfileReport')
Open index.html in the specified directory to browse the results.
Practical Code Examples
Basic Profiling Session
This complete example demonstrates profiling a custom function:
% Define a function with uneven workload
function result = unevenWork(n)
result = 0;
for i = 1:n
if mod(i, 2) == 0
pause(0.001); % Simulate even-numbered delay
end
result = result + i;
end
end
% Profile the execution
profile on
for k = 1:100
unevenWork(k);
end
profile off
% Display top 5 time consumers
profile('show', 'totaltime', 5)
Programmatic Analysis of Profile Data
Extract specific metrics for automated optimization testing:
p = profile('info');
% Find the function with maximum total time
[~, idx] = max([p.TotalTime]);
fprintf('Bottleneck: %s in %s\n', p(idx).FunctionName, p(idx).Filename);
% Calculate average calls per function
avgCalls = mean([p.NumCalls]);
fprintf('Average call count: %.1f\n', avgCalls);
Advanced C++ Integration
For builtin developers extending Nelson, manually instrument critical sections:
#include "Profiler.hpp"
void optimizedAlgorithm(Evaluator* eval, const ArrayOf& input)
{
// Access the singleton
Nelson::Profiler* pr = Nelson::Profiler::getInstance();
// Ensure profiling is active
if (pr->isActive()) {
uint64_t tic = pr->tic();
// ... perform algorithm work ...
// Build stack info (normally provided by evaluator)
std::vector<StackEntry> stack = eval->getCallStack();
pr->toc(tic, stack);
}
}
Key Implementation Files
Understanding the source structure helps when interpreting profiler behavior or contributing improvements.
| File Path | Purpose |
|---|---|
modules/profiler/builtin/cpp/profileBuiltin.cpp |
Implements the profile command dispatcher, handling subcommands like on, off, show, and info. |
modules/profiler/src/cpp/Profiler.cpp |
Core singleton implementation containing on(), off(), tic(), toc(), info(), show(), and save() methods. |
modules/profiler/src/include/Profiler.hpp |
Header defining the Profiler class, ProfileEntry struct, and Profile_Sort_Type enumeration. |
modules/profiler/help/en_US/xml/profile.xml |
Official documentation defining syntax, parameters, and usage examples for the builtin. |
modules/profiler/tests/script_to_profile.m |
Test fixture used by the test suite to validate profiler accuracy. |
Summary
- Activate profiling with
profile onto begin collecting timing data; the Nelson evaluator automatically instruments function calls and script lines. - Control overhead by limiting profiled sections with
profile offwhen measurement is complete, preserving data in the singletonProfilerinstance. - Analyze results using
profile('info')for structured data orprofile('show', sortOption, n)for formatted console output sorted by time, calls, or location. - Export reports via
profsaveto generate HTML files with per-function breakdowns for external review. - Extend functionality by referencing
modules/profiler/src/cpp/Profiler.cppandProfiler.hppto understand thetic/toctiming hooks and hash-based aggregation mechanism.
Frequently Asked Questions
How do I reset the profiler data between different code sections?
Call profile on again while the profiler is already running. According to the implementation in modules/profiler/src/cpp/Profiler.cpp (lines 44–56), the on() method automatically invokes clear(), which resets the profileMap hash map and deletes all previous ProfileEntry records. This ensures that subsequent measurements are not contaminated by earlier runs.
What is the difference between profile('info') and profile('show')?
profile('info') returns a struct array containing raw profiling data (FunctionName, Filename, LinePosition, NumCalls, TotalTime, PerCall) that you can manipulate programmatically, as implemented in Profiler::info(). In contrast, profile('show', sortOption, nbLines) calls Profiler::show(), which formats this data into a human-readable console table with headers and limited row counts, suitable for quick inspection during interactive sessions.
Can I profile individual lines within a function, or only whole functions?
The Nelson profiler captures data at the line level inside scripts and functions. When the evaluator executes code with profiling enabled, it invokes Profiler::tic() before and Profiler::toc() after each line or function call, recording the specific line position (LinePosition) in the ProfileEntry. This granularity allows you to identify specific bottlenecks within large functions, not just which functions are slow overall.
How do I export profiling results for sharing with team members?
Use the profsave builtin function to generate a self-contained HTML report. After collecting data with p = profile('info'), execute profsave(p, 'destinationFolder'). This invokes the Profiler::save() method (starting at line 85 in Profiler.cpp), which writes an index.html file plus per-file detail pages that can be opened in any web browser without requiring Nelson to be installed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →