• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

To With or Not To With?

Participant ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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.
TOPICS
Scripting

Views

1.3K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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!)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 20, 2008 Aug 20, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Aug 21, 2008 Aug 21, 2008

Copy link to clipboard

Copied

"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".

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 21, 2008 Aug 21, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 21, 2008 Aug 21, 2008

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 25, 2008 Aug 25, 2008

Copy link to clipboard

Copied

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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 26, 2008 Aug 26, 2008

Copy link to clipboard

Copied

LATEST
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.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines