Re: Avoid slow initialization of DynamicModule
- To: mathgroup at smc.vnet.net
- Subject: [mg84566] Re: Avoid slow initialization of DynamicModule
- From: "Kevin J. McCann" <Kevin.McCann at umbc.edu>
- Date: Thu, 3 Jan 2008 20:35:03 -0500 (EST)
- Organization: University System of Maryland
- References: <200712240946.EAA06236@smc.vnet.net> <fkt9h6$6f7$1@smc.vnet.net>
Thomas, Your code is really fantastic! I am running it on an AMD dual core 64-bit 2.6GHz and I don't see any slowness in your original code. Kevin Thomas Muench wrote: > Dear John, > > Thank you for your detailed reply, you suggestion worked like charm. > It is quite unexpected behavior at first, but when thinking about it, > it makes sense. > > I changed your code a little bit to restore the encapsulation, because > I really did not like the idea that you have to load your image data > into a given variable named "img", and that the program would fail any > other way. It is quite simple: > > stratification[image_]:= > Module[{img=image}, > DynamicModule[.... same code as before....] > ] > > You can now call the function with > stratification[whatever] > > and it will work. The only disadvantage is that the data will now be > in memory twice, namely as "img" (in the Module) and as "whatever" (as > global variable). For really *very* large image data that consumes to > much memory on my laptop for the kernel to survive. But other than > that it is fine. > > Thank you again very much > > thomas > > On Dec 25, 2007 6:47 AM, John Fultz <jfultz at wolfram.com> wrote: >> I'm happy to see users such as yourself creating such impressive interfaces with >> Mathematica. What you've done here is very nice, and does a great job of >> exploiting the new version 6 features. >> >> As to the source of your problems, you can forget any inherent overhead in >> Dynamic. The system scales extremely well, and the overhead it introduces is >> trivial. The principle sources of the typical Dynamic troubles have to do with >> what the Dynamic is evaluating. The problems I often see are... >> >> * Dynamic has dependencies on too many variables which are triggering too often. >> So the Dynamic recomputes much more often than necessary. This is the problem >> which can lead to 100% CPU usage despite the fact that the Dynamic doesn't >> appear to change (i.e. it keeps recalculating the same result over and over >> again). >> >> * Dynamic is given more to compute than necessary, making every triggered >> Dynamic too expensive to compute. >> >> * The computation Dynamic is given just requires too much time, and keep the >> front end and/or kernel in limbo while it's computing. >> >> You've set up your interface pretty well, and have avoided these problems. But >> due to the scale of your data, another factor is coming into play which is rare >> in most other Dynamic examples. >> >> When Mathematica sends a very large result from the kernel to the front end, >> things can start to really slow down and consume a lot of memory. Each of the >> following contributes to that slowdown, both due to computational overhead and >> memory consumption (which contributes to memory page faults)... >> >> * The kernel code which creates the typeset form of the expression >> * The transmission of the result over MathLink >> * The creation and representation of the result in the front end >> >> Your example unwittingly exposes the worst possible behaviors for all three of >> the above cases. When you do... >> >> img=<large thing> >> stratification[img] >> >> By default, this actually executes as... >> >> stratification[<large thing>] >> >> which is no problem for the kernel. But because of the way that function >> variable replacement works, this eventually becomes, e.g.... >> >> Dynamic[ListLinePlot[<large thing>]] (*I'm simplifying your code slightly here*) >> >> which then typesets into... >> >> DynamicBox[MakeBoxes[ListLinePlot[<large thing>], StandardForm]] >> >> which is then sent, in its entirety to the FE. The process of creating the >> DynamicBox[] is very expensive because of <large thing>, and then <large thing> >> is sent to the FE to be represented inside the Dynamic. Then the FE sends >> <large thing> back to the kernel in the evaluation of ListLinePlot, and the >> kernel finally sends the much more moderately-sized resulting graphic back. It >> is as if you took <large thing> and copied and pasted it directly in the >> definition of 'stratification'. Of course, this is even worse because <large >> thing> shows up in three separate Dynamics (the ListLinePlot, the main Raster, >> and the list of slice Rasters). >> >> To illustrate the point, as well as show a very simple workaround, here's a very >> simple change to make. Change... >> >> stratification[img_]:= >> >> to >> >> stratification[image_]:= >> >> but do *not* change any of the references in the body of stratification. Assign >> your source data to 'img' as you did in this email and re-evaluate everything in >> a fresh kernel. Now, instead of 'img' being replaced as a function variable, it >> will be used a global variable. Now, the evaluation will be... >> >> Dynamic[ListLinePlot[img]] >> >> which will typeset into >> >> DynamicBox[MakeBoxes[ListLinePlot[img], StandardForm]] >> >> and what will be sent to the FE will be no more than the 3 bytes of the variable >> name 'img' (plus another several bytes to qualify that it's in the Global` >> context). Then, the FE will evaluate >> >> ListLinePlot[img] >> >> and the value of img will never have been sent to the FE...only the graphic >> resulting from ListLinePlot[img]. And, in your example, this will actually be >> much smaller because you never create a graphical representation of all of img, >> but only pieces of it. I tried this with a random set of data of the size you >> describe, and it was extremely efficient...taking only a few seconds (on a very >> fast and new machine...your mileage may vary). >> >> Now, normally I would say this isn't the best practice. It's generally good for >> interfaces to well-encapsulated. They should use variables which reside in >> DynamicModules[] and not have side effects by using or creating global variables >> (unless using global variables is part of the purpose of the interface, of >> course). But there's no way to fully encapsulate this interface without the >> undesirable effects you're seeing. >> >> You could store a variable in a private context which represents the source data >> for the image. That would encapsulate it somewhat. But, for as long as you're >> going to have upwards of 500 megabytes of source data, you're going to have to >> make sure it doesn't get duplicated, typeset, or transmitted to the FE to be >> efficient. >> >> Sorry for the very long explanation, but I wanted to be as clear as possible. >> Hope you found it helpful. >> >> Sincerely, >> >> John Fultz >> jfultz at wolfram.com >> User Interface Group >> Wolfram Research, Inc. >> >> >> >> >> On Mon, 24 Dec 2007 04:46:19 -0500 (EST), Thomas Muench wrote: >>> Dear MathGroup, >>> >>> I have written an application (Mathematica 6.0.1 on Windows XP, code >>> see below) with which I want to analyze image data (3D stacks acquired >>> with a confocal microscope). The program works very nicely in >>> principle (with small sample data), but it does NOT work when the data >>> is of the size that I usually deal with. The dynamic display does not >>> even initialize. The computer works hard for about 40 minutes, and >>> then Mathematica quits without an error message. >>> My image stacks usually have dimension e.g. 63x4x512x512 (each image >>> has x-y-dimension 512x512, 4 color channels at each z-level, and on >>> the order of 60 levels). >>> >>> My dynamic display consists of 3 graphics (2 of which are >>> LocatorPanes, and 1 a simple ListLinePlot), which don't take too much >>> time to be created when I evaluate the corresponding graphic commands >>> outside of anything "Dynamic" (it takes 1 sec, 0.2 sec and .01 sec, >>> respectively). So the problem should not be the creation of the >>> graphics from the image stacks. I think it has something to do with >>> the overhead that is connected with dynamic displays, whatever that >>> may be. In fact, once the Dynamic interface is created in the >>> notebook, all the manipulations I do with it are quite swift. >>> >>> Here is some timing information that I obtained: To initialize the >>> display with sample data of size 63x4x50x50, it takes about 8 seconds. >>> 4 times as large (63x4x100x100) takes about 30 seconds, i.e. about 4 >>> times as long. If it would keep scaling linearly like that, I would >>> expect about 15 minutes for my full data, but, as mentioned, it >>> crashes after 40 minutes. I guess apart from the crash, linearity >>> brakes down because of memory issues. >>> >>> My question is: Why is there such a large overhead? Is there a way to >>> avoid it? In my particular case, I do not need "usability" of the >>> dynamic interface preserved over sessions. I need to quickly load and >>> display the data, do some manipulations to extract some numbers, and >>> then move on to the next data set. So even if it worked in 15 minutes >>> it would not be very useful. >>> >>> Since the problem scales with data size, I tried to use >>> LocalizeVariables->False, but this does not seem to be a valid option >>> for DynamicModule. Any other suggestions to get rid of (at least part >>> of) the overhead? >>> >>> Thank you very much, >>> thomas >> >> > -- Kevin J. McCann Research Associate Professor JCET/Physics Physics Building University of Maryland, Baltimore County 1000 Hilltop Circle Baltimore, MD 21250