Re: Re: Avoid slow initialization of DynamicModule
- To: mathgroup at smc.vnet.net
- Subject: [mg84482] Re: [mg84467] Re: [mg84436] Avoid slow initialization of DynamicModule
- From: John Fultz <jfultz at wolfram.com>
- Date: Fri, 28 Dec 2007 04:10:57 -0500 (EST)
- Reply-to: jfultz at wolfram.com
Well, this isn't quite encapsulated. Consider... In[1]:= Module[{img}, img] Out[1]= img$380 Here's a case where the implementation of Module[]'s scoping ends up leaving= a bread crumb. And this permanently remains in the namespace... In[2]:= Names["Global`img*"] Out[2]= {"img", "img$380"} Turns out that something very similar happens if you use a Module variable inside a Dynamic that is in the body of that Module... (* The DynamicModule here is unnecessary...just added it to make it similar= *) (* to your example *) In[3]:= Module[{img = 1}, DynamicModule[{x = 1}, Dynamic[img x]]] (* note that DynamicBox has img$701, a Module variable *) Out[3]= \!\(\* DynamicBox[ ToBoxes[$CellContext`img$701 $CellContext`x$$, StandardForm], ImageSizeCache->{7., {0., 8.}}]\) (* and sure enough, it shows up here, too *) In[4]:= Names["Global`img*"] Out[4]= {"img", "img$380", "img$701"} Sincerely, John Fultz jfultz at wolfram.com User Interface Group Wolfram Research, Inc. On Wed, 26 Dec 2007 05:10:10 -0500 (EST), 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