14 Replies Latest reply on Aug 26, 2008 6:00 AM by try67

    To With or Not To With?

    Level 1
      One of the first things I read when I started working on JavaScripting InDesign was "Don't use With constructions -- they're time-consuming and possibly dangerous."

      So, whenever I see people posting scripts with with statements in them, I smugly sit where feeling superior because I know they shouldn't while at the same time I envy how clean and easy their code is to read.

      So, here I am saving and restoring user ruler settings and I think to myself, this would be so much easier to read with a with statement. So let's time it and see how long it takes.

      I'll post the two versions of the script I ran in the first message.
        • 1. Re: To With or Not To With?
          Level 1
          First, doing it "my way":
          (function() {
          
            if (app.documents.length > 0 &&
                  app.selection.length > 0) {
              var aDoc = app.documents[0];
              var myStartTime = new Date();
              for (var j = 100; j >= 1; j--) {
                //save users measurement preferences
                var userHoriz = aDoc.viewPreferences.horizontalMeasurementUnits;
                var userVert =  aDoc.viewPreferences.verticalMeasurementUnits;
                aDoc.viewPreferences.horizontalMeasurementUnits = MeasurementUnits.points;
                aDoc.viewPreferences.verticalMeasurementUnits = MeasurementUnits.points;
                //restore users measurement preferences
                aDoc.viewPreferences.horizontalMeasurementUnits = userHoriz;
                aDoc.viewPreferences.verticalMeasurementUnits = userVert;
              }
              var myEndTime = new Date();
              var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
              alert(myDuration);
          }
          }())
          Result: Just over seven seconds -- consistently for a number of runs.

          So, how long does it take to do it this way:
          (function() {
          
            if (app.documents.length > 0 &&
                  app.selection.length > 0) {
              var aDoc = app.documents[0];
              var myStartTime = new Date();
              for (var j = 100; j >= 1; j--) {
                //save users measurement preferences
                with (aDoc.viewPreferences) {
                  var userHoriz = horizontalMeasurementUnits;
                  var userVert =  verticalMeasurementUnits;
                  horizontalMeasurementUnits = MeasurementUnits.points;
                  verticalMeasurementUnits = MeasurementUnits.points;
                }
                with (aDoc.viewPreferences) {
                //restore users measurement preferences
                  horizontalMeasurementUnits = userHoriz;
                  verticalMeasurementUnits = userVert;
                }
              }
              var myEndTime = new Date();
              var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
              alert(myDuration);
          }
          }())
          Just over seven seconds consistently across a number of runs. So that's wiped that smug smile off my face!

          Dave
          • 2. Re: To With or Not To With?
            Harbs. Level 6
            Hi Dave,

            I actually got (slightly) better results using your second ("with")
            construct. The thing is, you have no nesting in your "with" statement.
            Try changing that to:
            with(aDoc){
            with(viewPreferences){
            ........

            and you will start to see rapid degradation of performance. The more
            nesting you have, I believe the more drastic the degradation will be...

            I'll keep staying away from "with" statements... ;)

            Harbs
            • 3. Re: To With or Not To With?
              GagnonEric Level 1
              You surely have part of a answer in the timing results... as it's quite hard to know how things behave on the compiler side!

              I reminds me a problem i had with realbasic and OLE object, where using the full dot syntax to modify a few property of a same object gave horrible performance results (say, reading table values).

              Not having access to "with" construct (if i remind well), the solution i had to use was to keep a var of the level i was working with.

              So (in pseudo code), instead of:
              app.doc(x).element(y).prop1=value
              app.doc(x).element(y).prop2=value

              It was better to have

              var elementRef=app.doc(x).element
              elementRef.prop1=value
              elementRef.prop2=value

              Intuitively i would guess that the more deep the object you are trying to access, the more likely the full dot syntax would be costly to evaluate... but that is just a guess.

              Maybe you could give this approach a try? Using a reference to aDoc.viewPreferences? and see if it change the timing?

              ps: Considering that you have to use a big loop to make the timing difference noticeable, i would personnaly favor "readability" over unsignificative performance gain (or undocumented problems!)
              • 4. Re: To With or Not To With?
                Level 1
                Here's a case I ran into this afternoon where the with approach is definitely wrong:
                with (myTF.textFramePreferences) {
                
                  textColumnCount = 2;
                  textColumnGutter = 12;
                }
                But here this issue is not so much with the with construct as with the fact that doing this leads to two (in this case) interactions with the object model, while this:
                myTF.textFramePreferences.properties = {
                
                  textColumnCount: 2,
                  textColumnGutter: 12
                }
                requires only one.

                Dave
                • 5. Re: To With or Not To With?
                  Harbs. Level 6
                  Eric has a very good point!

                  When the four references to aDoc.viewPreferences are changed to a
                  variable, the time it took to run was cut in half -- way less than the
                  "with" statement!

                  The rule of creating variables for objects when they are accessed more
                  than once (especially when they nested in other objects), is almost as
                  important as the rule of not using "with" statements! :)

                  Harbs
                  • 6. Re: To With or Not To With?
                    Peter Kahrel Adobe Community Professional & MVP
                    Dave,

                    I'm not sure that you can say that your example of the with-statement in your post #4 interacts with the object model twice, and the alternative just once. It may look like that but maybe both alternatives interact with the OM just once internally.

                    Peter
                    • 7. Re: To With or Not To With?
                      Level 1
                      Astonishing:
                      myTF= app.selection[0];
                      
                      var myStartTime = new Date();
                      for (var j = 0; 100 > j; j++) {
                        myPrefs = myTF.textFramePreferences;
                        myPrefs = textColumnCount = 2;
                        myPrefs = textColumnGutter = 12;
                      }
                      var myEndTime = new Date();
                      var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
                      alert(myDuration)

                      var myStartTime = new Date();
                      for (var j = 0; 100 > j; j++) {
                        with (myTF.textFramePreferences) {
                          textColumnCount = 2;
                          textColumnGutter = 12;
                        }
                      }
                      var myEndTime = new Date();
                      var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
                      alert(myDuration)

                      var myStartTime = new Date();
                      for (var j = 0; 100 > j; j++) {
                        myTF.textFramePreferences.properties = {
                          textColumnCount: 2,
                          textColumnGutter: 12
                        }
                      }
                      var myEndTime = new Date();
                      var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
                      alert(myDuration)
                      First alert: 0.5 seconds, second: 2.9 seconds, third: 2.5 seconds.

                      Well, that's going to change the way I write some of my scripts.

                      Dave
                      • 8. Re: To With or Not To With?
                        Level 1
                        I just tried this:
                        myTF= app.selection[0];
                        
                        var myStartTime = new Date();
                        for (var j = 0; 100 > j; j++) {
                          myPrefs = myTF.textFramePreferences;
                          myPrefs.textColumnCount = 2;
                          myPrefs.textColumnGutter = 12;
                        }
                        var myEndTime = new Date();
                        var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
                        alert(myDuration)

                        var myStartTime = new Date();
                        for (var j = 0; 100 > j; j++) {
                          with (myTF.textFramePreferences) {
                            textColumnCount = 2;
                            textColumnGutter = 12;
                          }
                        }
                        var myEndTime = new Date();
                        var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
                        alert(myDuration)

                        var myStartTime = new Date();
                        for (var j = 0; 100 > j; j++) {
                          myTF.textFramePreferences.properties = {
                            textColumnCount: 2,
                            textColumnGutter: 12
                          }
                        }
                        var myEndTime = new Date();
                        var myDuration = (myEndTime - myStartTime)/1000; // Times are in milliseconds
                        alert(myDuration)
                        First loop: 3.0 seconds; Second loop: 2.8 seconds; Third loop: 2.2 seconds (all this on a G4/800MHz).

                        Dave
                        • 9. Re: To With or Not To With?
                          Harbs. Level 6
                          I always deal with "properties" if I can...

                          This is five times faster than the version with just: myPrefs =
                          myTF.textFramePreferences;

                          myTF= app.selection[0];
                          var myStartTime = new Date();
                          for (var j = 0; 100 > j; j++) {
                          myPrefs = myTF.textFramePreferences.properties;
                          myPrefs.textColumnCount = 2;
                          myPrefs.textColumnGutter = 12;
                          }
                          var myEndTime = new Date();
                          var myDuration = (myEndTime - myStartTime)/1000; // Times are in
                          milliseconds
                          alert(myDuration)

                          [Edit: Never mind. It doesn't work at all... :( ]

                          Harbs
                          • 10. Re: To With or Not To With?
                            GagnonEric Level 1
                            "But here this issue is not so much with the with construct as with the fact that doing this leads to two (in this case) interactions with the object model, while this:

                            myTF.textFramePreferences.properties = {
                            textColumnCount: 2,
                            textColumnGutter: 12
                            }
                            "

                            I'm fairly sure that there is a low level handling to remap the property list values to the object itself so i would think the other way would be better... more code but less work on the interpreter side.

                            The only way to be sure that such code is a optimisation would be to look at the opcode code the javascript is working with (i know this can be done with actionscript as there is a few decompiler, but i dont know if you can inspect the stack of the extendScript virtual machine to get similar information...).

                            ps: I have just seen the timing results... interessing as it goes against my "intuition".
                            • 11. Re: To With or Not To With?
                              Level 1
                              Fellow Scripters,

                              I think that Dave's tests a.) exercise too few properties (i.e., too small a sample size) to prove anything one way or another, and b.) exercise too low a level of nesting. Imagine, if you will, a script that looks more like this:

                              with(app){
                              with(document){
                              with(stories.item(1)){
                              with(characters.item(5)){

                              ...etc. I believe that you will see a significant speed difference once you start working with things like text formatting attributes in this kind of a construction.

                              It's not really the "with" construction as much as it is the stack of references that need to be resolved. That said, it is lots quicker than it used to be.

                              It also depends on the language--in AppleScript, using a properties record will be *much* faster than sending the properties one at a time.

                              Thanks,

                              Ole
                              • 12. Re: To With or Not To With?
                                Level 1
                                What surprised me, Ole, was that in spite of how little I was exercising the properties, it made a significant difference to the time.

                                Dave
                                • 13. Re: To With or Not To With?
                                  D Zurn Level 1
                                  Another reason to avoid "with" statements is that you can't be sure which variable is ultimately being referenced.

                                  Doug Crockford has lots of other "best practices" for JavaScript which make sense to me and that I've tried to follow. I run all my JavaScript through www.jslint.com

                                  http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/

                                  Quote:

                                  JavaScripts with statement was intended to provide a shorthand for writing recurring accesses to objects. So instead of writing

                                  > ooo.eee.oo.ah_ah.ting.tang.walla.walla.bing = true;
                                  > ooo.eee.oo.ah_ah.ting.tang.walla.walla.bang = true;

                                  You can write

                                  > with (ooo.eee.oo.ah_ah.ting.tang.walla.walla) {
                                  > bing = true;
                                  > bang = true;
                                  > }

                                  That looks a lot nicer. Except for one thing. There is no way that you can tell by looking at the code which bing and bang will get modifed. Will ooo.eee.oo.ah_ah.ting.tang.walla.walla be modified? Or will the global variables bing and bang get clobbered? It is impossible to know for sure.
                                  • 14. Re: To With or Not To With?
                                    try67 MVP & Adobe Community Professional
                                    If I might pipe in here, With statements are also dangerous in VB. I had a program that kept crashing with no apparant reason, until I changed one large block of a With statement to a regular object reference, and then the program ran just fine.

                                    Maybe it's better to assign a variable to an object, and then use that instead? So instead of using:

                                    > with (ooo.eee.oo.ah_ah.ting.tang.walla.walla) { bing = true; bang = true; }

                                    for this:

                                    > ooo.eee.oo.ah_ah.ting.tang.walla.walla.bing = true;

                                    use:

                                    > var myVar = ooo.eee.oo.ah_ah.ting.tang.walla.walla;
                                    > myVar.bing=true;

                                    This will also eliminate any confusions about whether bing is a property or a global variable.