Callback slow when large object array loaded into function context

4 views (last 30 days)
I have a strange performance problem when loading an array of handle objects into a function, then subsequently invoking WindowScrollWheelFcn. When scrolling the wheel quickly, the callback interval is very slow, seriously affecting GUI responsiveness. However, if I pass in the object array to the outer function, the callback gets called much more quickly. I'm not sure why.
I tried to whittle the problem down to some test code here, including a handle class, and a function that creates a figure and callback. I could NOT seem to eliminate the need for the Mouse Wheel callback. I tried timers, and calling the function handle directly, but I could not get the problem to occur. So the best way to see it is to run the little test function below, and scroll the mouse wheel on the figure window.
This is just a basically "empty" handle class, and I provided a Static convenience function to create an array of JustHandle objects, which are returned, and also saved to file JH.MAT.
classdef JustHandle < handle
methods (Static)
% Return an array of JustHandle objects, and also save to file.
function JH=MakeArray(NItems)
JH(NItems) = JustHandle(); % pre-allocae object array.
fprintf('Creating array with %d objects...\n', NItems);
for i=1:NItems
JH(i) = JustHandle();
end
fprintf('Saving to file JH.MAT...\n');
save JH JH
end
end
end
And the function that can either take the JH array as an argument, or load it from the JH.MAT file.
function TestSlowCallback(JH)
if nargin < 1
LD = load('JH.mat');
JH = LD.JH;
end
tic;
FIG=figure(200);
FIG.WindowScrollWheelFcn = @ScrollWheelCallback;
function ScrollWheelCallback(src,evt)
fprintf('In callback %.3f\n', toc); % Show time *BETWEEN* callbacks.
tic
end
end
At the command line, create the JustHandle array file, and call the test function:
JH=JustHandle.MakeArray(200000);
TestSlowCallback; % When called w/out arg, file is loaded, and performance is slow.
TestSlowCallback(JH); % When JH is passed in, performace is MUCH better.
With R2018a, when calling w/out arguments, so that load() is called, I get about 300mSec between calls to the ScrollWheel callback. When passing in JH, I get almost an order of magnitude faster, about 40mSec between calls.
Does anyone have an idea why this might be happening, and how to avoid the problem? I understand that function handles have a context that must be passed around, but I'm curious why the difference between loading and passing - isn't the context the same in both cases?
It does seem a bit quirky and obscure, but I wonder if people are getting bitten by it without realizing.
-----
One final note, if I "do something" with the @ScrollWheelCallbackFcn function handle, like return it from the outer TestSlowCallback() function, the problem goes away! So, I guess I have a workaround for the problem, but it just seems really obscure, and I'm not sure why it changes anything.
Thanks for any enlightenment that anyone can provide.
  3 Comments
Guillaume
Guillaume on 2 Apr 2019
"The only thing I can see that is different is that in the nested function case you also have LD in the workspace and because it is a nested function it has access to the parent workspace which, in the other case, contains only JH."
Clearing LD inside the if had no effect on performance with my testing, so while I also thought that it may have been the reason, it doesn't appear so.
Dale Roberts
Dale Roberts on 2 Apr 2019
Edited: Dale Roberts on 2 Apr 2019
Thanks Adam and Guillaume for having a look, and for running down the possibility that the extra LD variable was causing the problem. The delay seems to be proportional to the number of objects in the array, and not necessarily the size of the objects (can add a large DATA property, and it does not change the delay). I find it a little depressing that, on every handle function invocation, Matlab has to somehow "go through" all the data in the passed context, causing some sort of O(n) delay every time. I would hope that the context would just contain some sort of "pointer" to the data, and not require looping over all the objects on every call.

Sign in to comment.

Accepted Answer

Guillaume
Guillaume on 2 Apr 2019
I suspect you meant
JH=JustHandle.MakeArray(200000);
I can't explain the behaviour, however note that load is known to prevent some optimisations.
Also, if you make the callback a local function instead of a nested function, the problem also goes away
function TestSlowCallback(JH)
if nargin < 1
LD = load('JH.mat');
JH = LD.JH;
end
tic;
FIG=figure(200);
FIG.WindowScrollWheelFcn = @ScrollWheelCallback;
end
%local function instead of nested function
function ScrollWheelCallback(src,evt)
fprintf('In callback %.3f\n', toc); % Show time *BETWEEN* callbacks.
tic
end
I suspect that load somehow affects the perfomance of closures in matlab.
  4 Comments
Dale Roberts
Dale Roberts on 3 Apr 2019
I'm sorry, I posted the previous comment before testing thoroughly. Of course, if the function is un-nested, then the function handle "closure" no longer includes the local variables. So that approach will not work as a solution for me, since my GUI needs access to the main function's variables.
Adam
Adam on 4 Apr 2019
You should always be able to have a local function work where a nested function works, simply by passing in the variables it needs from what would otherwise be the parent workspace.

Sign in to comment.

More Answers (0)

Categories

Find more on Interactive Control and Callbacks in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!