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 in Profiler.cpp).
  • Profiler::toc(tic, stack) – Calculates elapsed nanoseconds and updates the corresponding entry in profileMap (lines 83–105).

Data Aggregation and Reporting

When you request results via profile('info'), the profiler:

  1. Walks profileMap to collapse duplicate entries
  2. Sorts data according to the specified criterion (SORT_BY_TOTALTIME, SORT_BY_LINE, etc.)
  3. 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 on to begin collecting timing data; the Nelson evaluator automatically instruments function calls and script lines.
  • Control overhead by limiting profiled sections with profile off when measurement is complete, preserving data in the singleton Profiler instance.
  • Analyze results using profile('info') for structured data or profile('show', sortOption, n) for formatted console output sorted by time, calls, or location.
  • Export reports via profsave to generate HTML files with per-function breakdowns for external review.
  • Extend functionality by referencing modules/profiler/src/cpp/Profiler.cpp and Profiler.hpp to understand the tic/toc timing 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →