I have written a XOJO program to perform a periodic task wherein every ½ sec or so, it will grab some data from an external instrument, perform some mathematical manipulations on that data, store the results in either 2048 channel 1 dimensional arrays or as items in a dictionary and then display the results in the text property of Label controls and as a graphics called from the Paint event of a Canvas control.
After 10 secs (approximately 20 cycles), all data is saved to a file and then cleared from memory by Redimming the arrays to 0 channels (and then back to 2048 channels) and reinitializing the dictionary by setting all values to either 0 or . I then restart the cycle collection. And repeat.
For the purposes of this post, I use a random number generator to generate the data. I have performed tests with my program, running on a Mac OS 10.11.6 and compiled as either 64 bit or 32 bit. I also place clocks at various points in my program to time how long various tasks take.
I have noticed that when I run in 64 bit, the amount of time spent painting the canvas graphics increases as the program runs. In the first few minutes of running, plotting takes around 10 msecs or so. However, after a few hours, the same task takes over 100 msecs and will continue to take longer as the program runs. I see a similar pattern, although not as extreme, with the displaying of the data in the Label controls of my display. The subroutines that are performing the mathematical manipulations do not take any longer.
I do not see this behavior when I compile as 32 bit. Although the program as a whole takes ~14% longer to complete a cycle.
Is there something I am missing with the 64 bit compilation in regard to drawing to the display?
Thank you in advance for your comments/answers.
As a quick test try redimming to -1 instead of 0 when you clean down the array. There might be some kind of long term memory fragmentation or bug that creeps in after a lot of redims. I have no idea if setting to -1 instead of 0 will make any difference but its worth a try. You could also try resetting the dictionary with Dictionary.Clear to start from fresh every time, it might be worth trying that in a separate test from the redim.
Thanks Julian. I will give this a try. Of course, it takes a bit of time to run the tests…
I would sample the process just after launch and when the slowdown occurs to see if you can spot what is taking longer.
You can do this via the Activity Monitor or the XCode Instruments app.
Thank you Kevin.
With the various clocks I have peppered throughout my program, I can see which routines are slowing down and it is the routines that are involved with writing to the display. I wonder if there is some sort of Mac display buffer that I need to pay attention to…
Also, the slowdown happens gradually slowly over time.
Could it not be the macOS AppNap feature?
Not AppNap as the program is running in the foreground.
I do turn off the screen saver and I set the energy Saver settings to keep the display always on and I prevent the hard disk from sleeping and I do not enable Power Nap.
In one of my tests, I forgot to set the display to never sleep and when the display went to sleep, the speed of the program plummeted to near zero.
Do you experience the slowdown in either methods (arrays/dictionary)?
If so, would your structure allow you to dim the arrays big enough to hold the data that could accumulate before it is saved? And then simply either null their values or just overwrite them by using a ptr instead of the arrays Ubounds?
I would suspect some kind of memory fragmentation too. If this should be the case, static sized arrays should help.
The data displayed in the canvas control is stored in an array that is always the same size (2048 channels). The number of dimensions is set by the user at the start of the program. The data is always an integer value and in the range of Int32.
The data that gets displayed in the label controls’ .text property is stored in the dictionary and it either a string, integer, or a double.
The slowdown happens in both routines that write to the display, although it is more pronounced in the drawing to the canvas control.
I am unfamiliar with ptr and memory blocks, but if they offer better “memory control”, I will give it a try.
I have a test running now and if I observe slowdown, I will pause the program and try a sudo purge command from the terminal. Then unpause the program and see if the speed resets.
Instead of drawing the data directly into the canvas, I now draw the data into a picture. I then set the canvas’s backdrop to the picture. I now find that the time to update the drawing in the canvas does not change over the program’s running time; approximately 10 ms over 14 hours of running time, with perhaps several thousands of calls to the canvas.paint event.
No I have to do the same thing to all the label control .text properties.
It might be a good idea to see if you can create a stand alone example project that demonstrates the problem and logging a bug in Feedback.
I made a little test app with 96 labels (plus another, so 97 labels) being set by a 10 ms timer (each showing the iteration). After 33,449 iterations I don’t see any slowdown (each iteration taking 0,006 sec)
Interesting. Try a 500 ms timer and run it for 14 hours. I may also have an issue in that every time I save the data to a file, I also display some summary results in a listbox, but it shouldn’t affect the display of the cycle data for it does not reference this listbox.
I found an interesting feature of the canvas backdrop property. When I used the Canvas paint event to create the picture and then set it to the canvas.backdrop property, the canvas backdrop does not update with the new picture until you call the paint event again.
So I changed my canvas to an imagewell and set the newly created picture as its image–instant update. Thanks to Sam Rowlands for posting a declare statement to get rid of the annoying white border of the imagewell at runtime (back in May 22 2017).
don’t use backdrop to display a picture… use g.drawpicture myPic,0,0 in the Paint Event
much cleaner and I’ll bet much FASTER too
I don’t know if this is related but I have a 32bit application which calculates spiral lengths. When it runs idle (not being used for much of the time), after hours it has eated the complete ram memory of the computer. It does not accumulate date. Just eat memory while it is even not being used, only waiting idle until someone use it. I suspect there are serious memory leaks in Xojo. Made with Xojo 2017 version 3 on windows.
I did not see this was about the Mac until I posted. Please, accept my apology for my overlooking.
Thanks to you all for your comments/suggestions.
I built a small test program to see if I could recreate what I was seeing with my larger program.
Every 500 ms, I use a random number generator to create series of numbers which get added into a histogram and then plotted in 4 different ways. I use the microseconds command to determine how long each plotting event takes and I update it on the screen in 2 different ways. Every 25 cycles, I complete a measurement. I output the results to a tab-delimited text file and append a listbox on the display.
The 4 histograms were generated as follows:
- Graphics Canvas: Draw to the display using graphics of the canvas.paint event.
- Object Canvas. Draw to the display using Object2D class using graphics of the canvas.paint event
- Image pic. An image well. I create a picture using graphics and set the ImageWell.image =pic
- Canvas pic. I create a picture using graphics then use the canvas.paint event to DrawPicture(pic, 0,0) to the canvas.
The display text updates after each cycle, the total number of cycles, the cycle number of the current measurement, the number of lines in my output file (and rows in the listbox), the total amount of time each of the drawings took for the current measurement, and the average amount of time each drawing took for the current measurement.
I update the text by either Formatting the label.text property to its corresponding value, or I update an ImageWell.image by creating a picture of the corresponding strings.
A few observations based on this dataset above.
- Drawing using Object2D is ~3x slower than graphics class (18 ms v 6 ms). This isnt much of a surprise as Object2d is a vector graphics and is much more versatile.
- Drawing to a picture and displaying the picture in an ImageWell took on average 3.5 ms and was by far the fastest way to create and display a graphic. Displaying the picture to the canvas with the DrawPicture command in the Canvas.paint event took on average 7 ms.
- It is faster to update text on a display by changing the label.text property rather than by drawing to a picture and displaying it in an image well (0.8 ms v 2.1 ms).
I was unable to recreate what I was observing in my larger program, so there must be something else going on that is driving the general slowdown. Doing the two things listed below, has solved my problem. I wasnt completely systematic in my approach so I do not necessarily know if one was more significant than the other.
As for my larger program and my original post, I have learned a couple of things:
- Too many calls to Window.refresh() and Control.RefreshRect(), will cause the program to slow down. I eliminated many of them that did not seem to be needed and saw general improvement in speed. This solved the slowdown related to the updating text on the display issue.
- Drawing graphics to a picture and setting the ImageWell.image=picture is much faster method to display the histogram plots and the amount of time to generate and display onto the screen does not increase as the program runs; unlike drawing to graphics using a canvas.paint event.
- Do not let the display or hard drive go to sleep. As an added precaution, I turned the screensaver off while the program is running.
Great info - can you also try with UseFocusRing enabled / disabled? In the past with 32 bit apps it would cause a 2x to 10x slowdown. See feedback://showreport?report_id=26903
BTW, where available, call Invalidate() instead of Refresh, as it’ll also be giving you much better performance and doesn’t hurt if you call it “too often” - it’ll delay the updating until the next event handling loop, though, so this won’t work if you need to refresh something in a tight loop.
Michael, my test app was compiled and run under 64 bit. I left the UseFocusRing property set to the default enabled during my test. I am not sure how significant that was for I purposely let the program run undisturbed (I didn’t touch the mouse after I started the program until I stopped it).