Old 10-12-2023, 01:30 PM   #1
Human being with feelings
cfillion's Avatar
Join Date: May 2015
Location: Québec, Canada
Posts: 5,087
Default Lua profiler (development tool for ReaScripts)

This is an instrumenting profiler for Lua ReaScripts. It measures function execution time and call frequency. The collected data may be displayed either as a call tree, a flat list or a flame graph. Eight separate profile slots are available for quick A-B comparison.

This script is provided in the form of a library. Search for "Lua profiler" in the ReaTeam Scripts default ReaPack repository. ReaImGui is required as a dependency for the profiler's user interface.

The following data is collected for each function (many columns are hidden in the default view: right-click on the table header to enable them):
  • Source file and line
  • Execution time (with % of total and % of parent)
  • Call count
  • Frame count
  • Time per call (min/avg/max)
  • Time per frame (min/avg/max)
  • Calls per frame (min/avg/max)
Basic usage

The highest-level API for measuring long-running scripts is:

local profiler = dofile(reaper.GetResourcePath() ..
  '/Scripts/ReaTeam Scripts/Development/cfillion_Lua profiler.lua')
reaper.defer = profiler.defer
profiler.attachToWorld() -- after all functions have been defined
Select one of the options in the "Acquisition" menu to begin measurement.

run launches the profiler's user interface. defer overrides reaper.defer to capture frame information (should be called exactly once per frame).


The attachToWorld function above injects instrumentation into all functions found in the global scope, local scope and within tables recursively.

This adds some overhead and a stack level to all of those functions for the profiler to collect call information. It may be desirable to add instrumentation only to select functions instead of everything.

There are several functions provided for this purpose:
  • profiler.attachTo(string var, nil|table opts)
  • profiler.detachFrom(string var, nil|table opts)
  • profiler.attachToLocals(nil|table opts)
  • profiler.detachFromLocals(nil|table opts)
  • profiler.attachToWorld()
  • profiler.detachFromWorld()
attachTo and detachFrom take a variable path as a string. Here are some examples:

profiler.attachTo('reaper')              -- all functions in the reaper table
profiler.detachFrom('reaper')            -- undoes the previous line
profiler.attachTo('reaper.GetTrack')     -- only this function
profiler.attachTo('gfx`meta')            -- all functions in getmetatable(gfx)
profiler.attachTo('gfx`meta.__newindex') -- getmetatable(gfx).__newindex
profiler.attachTo('foo.bar.baz', { recursive = false })
'opts' is an optional table to customize the variable lookup behavior. Supported keys and default values are:
  • recursive = true
    Whether to dig into subtables (maximum depth is 8).
  • search_above = true
    For {attachTo,detachFrom}Locals only: whether to look into scopes above the caller's.
  • metatable = true
    Whether to also attach to the value's metatable if present.
Intermediate usage

The following API is provided to control when measurements are taken for single-shot scripts or more advanced needs.

start and stop activates and deactivates data acquisition.
local profiler = ...

Use enter and leave to measure a specific block of code (eg. within a larger function). They can be nested. The given string identifier should be unique.
local foo = 4
bar = 4

  for i = 1, 1<<16 do foo = foo * 4 end
  for i = 1, 1<<16 do bar = bar * 4 end
Advanced usage

There are a few more features exposed to the target script. The defer function shown in the "Basic usage" section above is actually a shortcut for start + stop + frame. frame increments the frame counter and updates per-frame timings.
-- user code here
To start data acquisition over multiple frames when using the defer override:
profiler.auto_start = true  -- start until manually stopped
profiler.auto_start = 60    -- start for 60 frames then stop automatically
profiler.auto_start = false -- stop
The profiler's user interface can be embedded into any ReaImGui script using showProfile.
profiler.showProfile(ctx, 'my_profile', width, height, child_flags])
The run function is a shortcut for creating a ReaImGui context and calling showWindow in a loop.
local open = profiler.showWindow(ctx, true, flags)
Call clear to reset all data in the selected profile.

Last edited by cfillion; 05-01-2024 at 03:31 PM. Reason: released version 1.1
cfillion is offline   Reply With Quote
Old 10-12-2023, 02:50 PM   #2
Meo-Ada Mespotine
Human being with feelings
Meo-Ada Mespotine's Avatar
Join Date: May 2017
Location: Leipzig
Posts: 6,665

This is huge!
Use you/she/her.Ultraschall-Api Lua Api4Reaper - Donate, if you wish

On vacation for the time being...
Meo-Ada Mespotine is offline   Reply With Quote
Old 10-12-2023, 04:23 PM   #3
Human being with feelings
deeb's Avatar
Join Date: Feb 2017
Posts: 4,874

Originally Posted by Meo-Ada Mespotine View Post
This is huge!
true ! amazing cfillion
deeb is offline   Reply With Quote
Old 10-12-2023, 11:39 PM   #4
Human being with feelings
FeedTheCat's Avatar
Join Date: May 2019
Location: Berlin
Posts: 2,266

Wow, this is great!

I was surprised to find out that reaper.MIDIEditor_GetActive is even slower than reaper.MIDI_GetRecentInputEvent...
Featured scripts: REAPER Update UtilityLil ChordboxGridbox/Adaptive gridMX TunerRS5K LinkMIDI Editor Magic Donate💝: PayPal|ko-fi
FeedTheCat is online now   Reply With Quote
Old 10-13-2023, 12:48 AM   #5
Human being with feelings
Join Date: Jul 2010
Location: Slovakia
Posts: 2,588

w o w
bFooz is offline   Reply With Quote
Old 10-13-2023, 08:33 AM   #6
Human being with feelings
dsyrock's Avatar
Join Date: Sep 2018
Location: China
Posts: 570

dsyrock is offline   Reply With Quote
Old 10-13-2023, 12:14 PM   #7
Human being with feelings
Fabian's Avatar
Join Date: Sep 2008
Location: Sweden
Posts: 7,464

I never always did the right thing, but all I did wasn't wrong...
Fabian is offline   Reply With Quote
Old 10-14-2023, 04:30 AM   #8
Human being with feelings
amagalma's Avatar
Join Date: Apr 2011
Posts: 3,539

This is great! Thank you very much!!

Here is a small test comparing the various ways of working with reaper arrays in Lua and the results:
Most of my scripts can be found in ReaPack.
If you find them useful, a donation would be greatly appreciated! Thank you! :)

Last edited by amagalma; 10-14-2023 at 05:28 AM.
amagalma is offline   Reply With Quote
Old 10-14-2023, 05:21 AM   #9
Human being with feelings
heda's Avatar
Join Date: Jun 2012
Location: Spain
Posts: 7,310

this is amazing. I think it will be very helpful to find performance bugs
thank you !
heda is offline   Reply With Quote
Old 12-04-2023, 09:10 AM   #10
Human being with feelings
Join Date: Mar 2016
Location: Italy
Posts: 359
Default This is amazing

I just started using this script yesterday,
honestly I was scared of what I could find on a script I'm developing,
but in less than 24h my script is already way faster than before.

thanks for developing this, it's huge!

I just wanted to report some discoveries here,

lua math functions vs. scripted math functions

more infos and code snippet on this post

Attached Images
File Type: png Screenshot 2023-12-04 at 17.04.51.png (90.9 KB, 577 views)

Last edited by 80icio; 12-04-2023 at 10:00 AM.
80icio is offline   Reply With Quote
Old 05-01-2024, 03:40 PM   #11
Human being with feelings
cfillion's Avatar
Join Date: May 2015
Location: Québec, Canada
Posts: 5,087

Released version 1.1:

• Add an optional child_flags parameter to profiler.showProfile()
• Add profiler.runloop (alias of profiler.defer)
• Add a flame graph view mode (enable in the Profile menu)
• Fix view mode and sort options not applying to inactive profiles
• Highlight the entire row on hover in the tree view mode
• Rename profiler.reset() to .clear()
• Report API usage errors at the user's call site

Last edited by cfillion; 05-01-2024 at 03:55 PM.
cfillion is offline   Reply With Quote

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT -7. The time now is 02:03 PM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.