So I thought I’d release my first plugin today,
but for the sake of improvement, I’ve completely screwed it up.
Everything I needed worked beautifully before I thought I’d also want to save the settings.
It occurred to me to use the Document class instead of global variables.
All variables I replaced the references to Document. But I didn’t do that.
For example FREQUENCY_1 I referred to Document () Config.osc.frequency1.value everywhere in the code.
But because of this my code has slowed down by at least 1000% !!!
It is normal ?
I don’t quite remember how significant the ‘overhead’ of accessing document properties is, but it is slower than a normal variable for sure.
Quick fix: try to just ‘cache’ the value as a normal variable wherever it is being heavily used/read. (small sidenote: is there any chance that you’re reading the value way too frequent? Any room for optimization?)
PS. Normally, if I used a document for saving data, I would only prepare it just before saving. Not as a data container for very frequent access. The most useful things of the document class (except for save_as) are the bangs/observability, imho. And perhaps an occasional value bind.
I’m already working on it.
I just have to load the values from the loaded Document into the variables and change the document only when saving. I had no idea it would be so slow.
There should be no caching necessary. The only possible extra overhead of the document is saving or rebuilding the document constantly, which never is done automatically - by default only happens before a tool gets unloaded or when you trigger a save manually.
An advantage of the document values is that you can bind them directly to the GUI widgets. Then the GUI automatically gets updated when the document value changes and the document automatically gets updated when the value in the GUI changes.
If you don’t mind sharing your slow version of the tool, we could have a more detailed look at this.
--------------------------------------------------------------------------------
-- documents_and_views
-- as already noted in 'available_controls'. views can also be attached to
-- external document values, in order to seperate the controller code from the
-- view code. We're going to do this tight now and do start by create a very
-- simple example document. Please have a look at Renoise.Document.API for more
-- detail about such documents
-- DOCUMENT
-- create a simple document
local example_document = renoise.Document.create("ExampleDocument") {
my_flag = false,
some_velocity = 127,
pad_x = 0.5,
pad_y = 0.5
}
-- notifier callbacks
local function my_flag_notifier()
local my_flag_value = example_document.my_flag.value
show_status(("'my_flag' changed to '%s' by either the GUI "..
"or something else..."):format(my_flag_value and "True" or "False"))
end
local function some_velocity_notifier()
local some_velocity = example_document.some_velocity.value
show_status(("'some_velocity' value changed to '%s' by either the GUI "..
"or something else..."):format(some_velocity))
end
local function pad_value_notifier()
local x, y = example_document.pad_x.value, example_document.pad_y.value
show_status(("'pad_xy' value changed to '%.2f,%.2f' by either the GUI "..
"or something else..."):format(x, y))
end
-- attach to the document
example_document.my_flag:add_notifier(my_flag_notifier)
example_document.some_velocity:add_notifier(some_velocity_notifier)
example_document.pad_x:add_notifier(pad_value_notifier)
example_document.pad_y:add_notifier(pad_value_notifier)
-- GUI
function documents_and_views()
local vb = renoise.ViewBuilder()
local DIALOG_MARGIN = renoise.ViewBuilder.DEFAULT_DIALOG_MARGIN
local CONTENT_SPACING = renoise.ViewBuilder.DEFAULT_CONTROL_SPACING
-- now we pass over the document struct to the views
local checkbox_row = vb:row {
vb:text {
text = "my_flag",
width = 80
},
vb:checkbox {
bind = example_document.my_flag --> bind
}
}
local valuebox_row = vb:row {
vb:text {
text = "some_velocity",
width = 80
},
vb:valuebox {
bind = example_document.some_velocity, --> bind
min = 0,
max = 0x7f
}
}
local xypad_row = vb:row {
vb:xypad {
bind = { --> bind
x = example_document.pad_x,
y = example_document.pad_y
},
width = valuebox_row.width,
}
}
renoise.app():show_custom_dialog(
"Documents & Views",
vb:column {
margin = DIALOG_MARGIN,
spacing = CONTENT_SPACING,
uniform = true,
vb:column {
spacing = CONTENT_SPACING,
checkbox_row,
valuebox_row
},
xypad_row
}
)
end
I’m surprised. Reading example_document.pad_x.value is magnitudes slower than if stored in a temporary variable (on my computer). Maybe it’s over-optimization in most cases, idk.
Just reading a document value won’t be the problem, but a document (class) property lookup will indeed also create some overhead. Just like calling one of multiple Lua or Renoise Lua API functions.
But this then is more a general optimization problem: there’s no need to do something again and again in a loop body, if the result is always the same.
Well, exactly as you said:
There’s no need to create a complete, second level cache for all document values. Simply copy & cache them when it really hurts.
For example:
function slow_function()
for i=1,1000,1 do
calculate_something(my_document.some_property.value)
end
end
function get_some_property_value()
return 55
end
function another_slow_function()
for i=1,1000,1 do
calculate_something(get_some_property_value())
end
end
function faster_function()
local some_property = my_document.some_property.value
for i=1,1000,1 do
calculate_something(some_property)
end
end
function another_faster_function()
local some_property = get_some_property_value()
for i=1,1000,1 do
calculate_something(some_property)
end
end
Moving anything (Lua function calls, Renoise API calls) out of the loop, when the result does not change within the loop, of course always will be faster. If that’s relevant or necessary, depends on how heavy or time critical the loop actually is. When for example calculating a new sample buffer, this indeed will make a big difference in summary. In general, it won’t.
Reading this again, this maybe exactly was the problem?
And I seem to have created more confusion than being helpful here. Sorry guys.
Hopefully the examples help to clear this up.
hello,
I’ll try it. thanks.
I was looking for something to avoid rehashing when writing to the table. But it seems that table.setn() is deprecated.May be this helps.