<?xml version="1.0" encoding="utf-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Looping with Python in XSI</title>
	<atom:link href="http://www.softimageblog.com/archives/31/feed" rel="self" type="application/rss+xml" />
	<link>http://www.softimageblog.com/archives/31#utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=looping-with-python-in-xsi</link>
	<description>People and thoughts behind Softimage in production...</description>
	<lastBuildDate>Fri, 23 Jul 2010 12:23:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>By: Rafe Sacks</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-17312</link>
		<dc:creator>Rafe Sacks</dc:creator>
		<pubDate>Fri, 18 Jul 2008 03:54:22 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-17312</guid>
		<description>Hi,

The conversation that has taken place in the replies to this article are important in defining how the article&#039;s information should be applied, however, you probably lost most beginners and a good portion of everyone else by the end of it ;). This article is great (with a few disclaimers warning about assumed usage)!

As someone already mentioned, readability should win the day when speed of various methods is either too close to worry about, or simply unnecessary. I would suggest creating good, clean, easy to read, and edit, code that works first, then optimize where necessary. Especially when working with a group of people that have to work with your code...and especially when working to a deadline. In production a developer can&#039;t always write awesome code just to stroke their &quot;ego&quot;, you have to spend the time where the production will best benefit. This is a hard-won lesson and can apply to anything from writing code to animating.

In python, I always reach for the object iterating for-loop. I find range-based looping is rarely something I will use by choice (in JScript it was the primary looping method, but while learning Python I read a book which said I would find that I simply would&#039;t need it most of the time. I have found this to be true). I would only move to other methods of looping to address performance issues. This is where this article gives some great clues to new XSI/Python users.

Nice one!

- Rafe</description>
		<content:encoded><![CDATA[<p>Hi,</p>
<p>The conversation that has taken place in the replies to this article are important in defining how the article&#8217;s information should be applied, however, you probably lost most beginners and a good portion of everyone else by the end of it ;). This article is great (with a few disclaimers warning about assumed usage)!</p>
<p>As someone already mentioned, readability should win the day when speed of various methods is either too close to worry about, or simply unnecessary. I would suggest creating good, clean, easy to read, and edit, code that works first, then optimize where necessary. Especially when working with a group of people that have to work with your code&#8230;and especially when working to a deadline. In production a developer can&#8217;t always write awesome code just to stroke their &#8220;ego&#8221;, you have to spend the time where the production will best benefit. This is a hard-won lesson and can apply to anything from writing code to animating.</p>
<p>In python, I always reach for the object iterating for-loop. I find range-based looping is rarely something I will use by choice (in JScript it was the primary looping method, but while learning Python I read a book which said I would find that I simply would&#8217;t need it most of the time. I have found this to be true). I would only move to other methods of looping to address performance issues. This is where this article gives some great clues to new XSI/Python users.</p>
<p>Nice one!</p>
<p>- Rafe</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Raffaele Fragapane</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-574</link>
		<dc:creator>Raffaele Fragapane</dc:creator>
		<pubDate>Tue, 20 Dec 2005 00:16:47 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-574</guid>
		<description>ops... I should also add that you shouldn&#039;&#039;t limit the domain of your thinking to the specifics of this one example.
this one case uses the position array because it was the most convenient way for me to generate the need for a large list in XSI and provide an example rooted in day2day needs.

while xsi almost always returns tuples when dealing with returning arrays to the user, there are a lot of instances where you could be working with external data as well, and that could very well return a list right away.

this example aside though, often one ends up applying these techniques and tricks to custom data that won&#039;&#039;t necessary be a tuple, nor easily pre-determined in its layout (IE: especially when working with databases and walking one you never know with how many branching offs you will end up with in some searches), but that is probably out of the scope of the article.</description>
		<content:encoded><![CDATA[<p>ops&#8230; I should also add that you shouldn&#8221;t limit the domain of your thinking to the specifics of this one example.<br />
this one case uses the position array because it was the most convenient way for me to generate the need for a large list in XSI and provide an example rooted in day2day needs.</p>
<p>while xsi almost always returns tuples when dealing with returning arrays to the user, there are a lot of instances where you could be working with external data as well, and that could very well return a list right away.</p>
<p>this example aside though, often one ends up applying these techniques and tricks to custom data that won&#8221;t necessary be a tuple, nor easily pre-determined in its layout (IE: especially when working with databases and walking one you never know with how many branching offs you will end up with in some searches), but that is probably out of the scope of the article.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Raffaele Fragapane</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-573</link>
		<dc:creator>Raffaele Fragapane</dc:creator>
		<pubDate>Tue, 20 Dec 2005 00:08:09 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-573</guid>
		<description>

yes, you have no choice but to make copies for tuples.
however, creating a list by walking the tuple, processing a row, and appending a new entry can be slower then casting the tuple to a list and modifying in place with another technique.

casting a tuple to a list by list() or creating a pre-determined sized list through list comprehension will be nearly instantaneous; and then you can modify it in place.
the speed boost will still be considerable over creting the list by processing and appending the entry at every cycle.

Also, if you multiply a None in a list I think that triggers a python optimization to generate a presized list even faster, something like myList = [None]*Size, but don&#039;&#039;t quote me on this one as I don&#039;&#039;t remember where or when I read it, and using list comp is usually so fast that I never concerned myself with finding a better way to pre-size a list.</description>
		<content:encoded><![CDATA[<p>yes, you have no choice but to make copies for tuples.<br />
however, creating a list by walking the tuple, processing a row, and appending a new entry can be slower then casting the tuple to a list and modifying in place with another technique.</p>
<p>casting a tuple to a list by list() or creating a pre-determined sized list through list comprehension will be nearly instantaneous; and then you can modify it in place.<br />
the speed boost will still be considerable over creting the list by processing and appending the entry at every cycle.</p>
<p>Also, if you multiply a None in a list I think that triggers a python optimization to generate a presized list even faster, something like myList = [None]*Size, but don&#8221;t quote me on this one as I don&#8221;t remember where or when I read it, and using list comp is usually so fast that I never concerned myself with finding a better way to pre-size a list.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Bernard Lebel</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-572</link>
		<dc:creator>Bernard Lebel</dc:creator>
		<pubDate>Mon, 19 Dec 2005 17:07:04 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-572</guid>
		<description></description>
		<content:encoded><![CDATA[<p>&gt;&gt; they’d be pretty much in line with yours for this particular example, infact yours are slightly more flattering to mapping then what I got on my 4 years old laptop, which is what I wrote and tested the script on.<br />
would you still want me to post them?</p>
<p>[Bernard] I think you answered my qestion, I was interested in the time curve on your side of things. Thanks for that.</p>
<p>&gt;&gt; (no need to swear on what the purpose is, as I said the point was never to try and belittle you, otherwise I would have brought forward the subject of poutine, not mapping functions).</p>
<p>[Bernard] mmmm poutine&#8230;. What a fascinating subject, isn&#8221;t it? That said, I can hardly see how you could belittle me (or a Canadian) on such a subject. ;)</p>
<p>&gt;&gt; IE: manipulating in place can be a lot faster then creating by appending while the loop runs, but you won’t always be able to pre-determine the size of the list in all dimensions (especially for very generically purposed pieces of code), or it would need counter-productive procedures or just too much time to test it in all situations.</p>
<p>Okay here I have a question for you. Unless I have missed something, there is no way to specify a list length in Python like in VBScript or JScript. On the other hand, the tuple has a fixed length, and its elements can&#8221;t be modified in-place. So I&#8221;m not sure what you meant with that statement&#8230;&#8230; if you are dealing with a list, then its length doesn&#8221;t really matter, but on the other hand, if you are dealing with a tuple, then you have no choice but to make copies. Correct?</p>
<p>&gt;&gt; I’ll be happy to acknowledge the last paragraph of the initial post in regards to my comment and consider it crossed out.</p>
<p>[Bernard] Thank you.</p>
<p>&gt;&gt; does this make my initial point any clearer or is there anything I left out or mis-interpreted/mis-presented.</p>
<p>Fair enough, I think we have reached a consensus and finally seem to be talking the same language by now.</p>
<p>Cheers<br />
Bernard</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Raffaele Fragapane</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-565</link>
		<dc:creator>Raffaele Fragapane</dc:creator>
		<pubDate>Mon, 19 Dec 2005 05:44:08 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-565</guid>
		<description>they&#039;&#039;d be pretty much in line with yours for this particular example, infact yours are slightly more flattering to mapping then what I got on my 4 years old laptop, which is what I wrote and tested the script on.
would you still want me to post them? (no need to swear on what the purpose is, as I said the point was never to try and belittle you, otherwise I would have brought forward the subject of poutine, not mapping functions).

in other cases, especially on much more complex operations then a simple addition, the difference map can make will go up tenfold, and we&#039;&#039;ve had cases where just mapping a function, with no other optimization, boosted a snippet&#039;&#039;s speed by well over x10.

in some other cases it wouldn&#039;&#039;t make much of a difference, and in some (although seldomly encountered) others a lambda could even slow down things a bit (the peculiarity of lambda functions is the way the process is mapped ahead of execution, and in cases where this process is voided, or not applied to a large enough table, it can be counterproductive).

Different techniques will require different steps.

IE: manipulating in place can be a lot faster then creating by appending while the loop runs, but you won&#039;&#039;t always be able to pre-determine the size of the list in all dimensions (especially for very generically purposed pieces of code), or it would need counter-productive procedures or just too much time to test it in all situations.

also, if you were to take away from the equation the time needed to create or manipulate lists and other elements which differ from technique to technique, results would change again, and while knowing the performance of each part of the execution can be relevant, in the end it&#039;&#039;s the performance of the whole, possibly in situations that are real-world related, that matters.

this is, obviously, neglecting other important elements of software design, but for sheer speed based operations (like manipulating geometry) the self-contained example is quite pertinent.

I&#039;&#039;ll be happy to acknowledge the last paragraph of the initial post in regards to my comment and consider it crossed out.
does this make my initial point any clearer or is there anything I left out or mis-interpreted/mis-presented.

cheers.
Raff</description>
		<content:encoded><![CDATA[<p>they&#8221;d be pretty much in line with yours for this particular example, infact yours are slightly more flattering to mapping then what I got on my 4 years old laptop, which is what I wrote and tested the script on.<br />
would you still want me to post them? (no need to swear on what the purpose is, as I said the point was never to try and belittle you, otherwise I would have brought forward the subject of poutine, not mapping functions).</p>
<p>in other cases, especially on much more complex operations then a simple addition, the difference map can make will go up tenfold, and we&#8221;ve had cases where just mapping a function, with no other optimization, boosted a snippet&#8217;&#8217;s speed by well over x10.</p>
<p>in some other cases it wouldn&#8221;t make much of a difference, and in some (although seldomly encountered) others a lambda could even slow down things a bit (the peculiarity of lambda functions is the way the process is mapped ahead of execution, and in cases where this process is voided, or not applied to a large enough table, it can be counterproductive).</p>
<p>Different techniques will require different steps.</p>
<p>IE: manipulating in place can be a lot faster then creating by appending while the loop runs, but you won&#8221;t always be able to pre-determine the size of the list in all dimensions (especially for very generically purposed pieces of code), or it would need counter-productive procedures or just too much time to test it in all situations.</p>
<p>also, if you were to take away from the equation the time needed to create or manipulate lists and other elements which differ from technique to technique, results would change again, and while knowing the performance of each part of the execution can be relevant, in the end it&#8217;&#8217;s the performance of the whole, possibly in situations that are real-world related, that matters.</p>
<p>this is, obviously, neglecting other important elements of software design, but for sheer speed based operations (like manipulating geometry) the self-contained example is quite pertinent.</p>
<p>I&#8221;ll be happy to acknowledge the last paragraph of the initial post in regards to my comment and consider it crossed out.<br />
does this make my initial point any clearer or is there anything I left out or mis-interpreted/mis-presented.</p>
<p>cheers.<br />
Raff</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Bernard Lebel</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-564</link>
		<dc:creator>Bernard Lebel</dc:creator>
		<pubDate>Mon, 19 Dec 2005 02:32:10 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-564</guid>
		<description></description>
		<content:encoded><![CDATA[<p>Most interesting, Raffaele. Perhaps I&#8221;m pushing my luck, but I would like to see a set of timing results, if you don&#8221;t mind. Simply to compare with mine, I swear.</p>
<p>#INFO : for range appending the new value 0.383707485695<br />
#INFO : check<br />
#INFO : for each element and appending 0.321049263254<br />
#INFO : check<br />
#INFO : for range modifying in place 0.337336047004<br />
#INFO : check<br />
#INFO : mapping the function 0.170906125977<br />
#INFO : check<br />
#INFO : mapping a lambda function 0.147533536157<br />
#INFO : check<br />
#INFO : list comprehension 0.222757300532<br />
#INFO : check<br />
#INFO : list comprehension with embedded function 0.11899168944<br />
#INFO : check<br />
#INFO : total time elapsed: 2.75101092793</p>
<p>&gt;&gt; I said the way your statements try to encompass the whole looping techniques thing is what I’m arguing.</p>
<p>I would like to point you to the last paragraph of my initial post. But I take your point nonetheless.</p>
<p>Thanks for the code, I appreciate.</p>
<p>Bernard</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Raffaele Fragapane</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-547</link>
		<dc:creator>Raffaele Fragapane</dc:creator>
		<pubDate>Wed, 14 Dec 2005 10:32:48 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-547</guid>
		<description>and I&#039;&#039;m a dummy for having forgot the syntax of list comprehension and having had two typos in the script....
here&#039;&#039;s the correct version, ignore the previous one and the lambda thing, was in muppet mode and didn&#039;&#039;t test things.

&lt;code&gt;import win32com
import time&lt;/code&gt;

&lt;code&gt;def simpleAddition(foo):
&#160;&#160;&#160;&#160;foo = foo +1
&#160;&#160;&#160;&#160;return foo&lt;/code&gt;

&lt;code&gt;#Application.CreatePrim(&quot;Sphere&quot;, &quot;MeshSurface&quot;, &quot;&quot;, &quot;&quot;)
#Application.SetValue(&quot;sphere.polymsh.geom.subdivu&quot;, 300, &quot;&quot;)
#Application.SetValue(&quot;sphere.polymsh.geom.subdivv&quot;, 300, &quot;&quot;)&lt;/code&gt;


&lt;code&gt;overallStartTime = time.clock()&lt;/code&gt;

&lt;code&gt;tupPointsPosition = Application.Selection(0).ActivePrimitive.Geometry.Points.PositionArray&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
forRangeAppendPointsPosition = [[],[],[]]
for i in range(len(tupPointsPosition[0])):
&#160;&#160;&#160;&#160;forRangeAppendPointsPosition[0].append(simpleAddition(tupPointsPosition[0][i]))
&#160;&#160;&#160;&#160;forRangeAppendPointsPosition[1].append(simpleAddition(tupPointsPosition[1][i]))
&#160;&#160;&#160;&#160;forRangeAppendPointsPosition[2].append(simpleAddition(tupPointsPosition[2][i]))
endTime = time.clock()
print &quot;for range appending the new value         &quot; + str(endTime-startTime)
testList = forRangeAppendPointsPosition
if testList == forRangeAppendPointsPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
forInPointPosition = [[],[],[]]
for i in tupPointsPosition[0]:
&#160;&#160;&#160;&#160;forInPointPosition[0].append(simpleAddition(i))
for i in tupPointsPosition[1]:
&#160;&#160;&#160;&#160;forInPointPosition[1].append(simpleAddition(i))
for i in tupPointsPosition[2]:
&#160;&#160;&#160;&#160;forInPointPosition[2].append(simpleAddition(i))
endTime = time.clock()
print &quot;for each element and appending            &quot; + str(endTime-startTime)
if testList == forInPointPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
forRangeInPlacePointsPosition = [list(tupPointsPosition[0]),list(tupPointsPosition[1]),list(tupPointsPosition[2])]
for i in range(len(tupPointsPosition[0])):
&#160;&#160;&#160;&#160;forRangeInPlacePointsPosition[0][i] = simpleAddition(tupPointsPosition[0][i])
&#160;&#160;&#160;&#160;forRangeInPlacePointsPosition[1][i] = simpleAddition(tupPointsPosition[1][i])
&#160;&#160;&#160;&#160;forRangeInPlacePointsPosition[2][i] = simpleAddition(tupPointsPosition[2][i])
endTime = time.clock()
print &quot;for range modifying in place              &quot; + str(endTime-startTime)
if testList == forRangeInPlacePointsPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;#no for each modifying in place as retrieving the index of the value
#makes modification in place unreliable and dangerous
#or overly complicated and slow to make it safe&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
mapPosX = map(simpleAddition, tupPointsPosition[0])
mapPosY = map(simpleAddition, tupPointsPosition[1])
mapPosZ = map(simpleAddition, tupPointsPosition[2])
mapPointsPosition = [mapPosX, mapPosY, mapPosZ ]
endTime = time.clock()
print &quot;mapping the function                      &quot; + str(endTime-startTime)
if testList == mapPointsPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
mapLambdaPosX = map(lambda x: x+1, tupPointsPosition[0])
mapLambdaPosY = map(lambda x: x+1, tupPointsPosition[1])
mapLambdaPosZ = map(lambda x: x+1, tupPointsPosition[2])
mapLambdaPointsPosition = [mapPosX, mapPosY, mapPosZ ]
endTime = time.clock()
print &quot;mapping a lambda function                 &quot; + str(endTime-startTime)
if testList == mapLambdaPointsPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
listCompPointsPosition = [[simpleAddition(i) for i in tupPointsPosition[0]],[simpleAddition(i) for i in tupPointsPosition[1]],[simpleAddition(i) for i in tupPointsPosition[2]]]
endTime = time.clock()
print &quot;list comprehension                        &quot; + str(endTime-startTime)
if testList == listCompPointsPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;startTime = time.clock()
listCompDirectPointsPosition = [[i+1 for i in tupPointsPosition[0]],[i+1 for i in tupPointsPosition[1]],[i+1 for i in tupPointsPosition[2]]]
endTime = time.clock()
print &quot;list comprehension with embedded function &quot; + str(endTime-startTime)
if testList == listCompDirectPointsPosition: print&quot;check&quot;&lt;/code&gt;

&lt;code&gt;overallEndTime = time.clock()
print &quot;total time elapsed:  &quot; + str(overallEndTime-overallStartTime)&lt;/code&gt;</description>
		<content:encoded><![CDATA[<p>and I&#8221;m a dummy for having forgot the syntax of list comprehension and having had two typos in the script&#8230;.<br />
here&#8217;&#8217;s the correct version, ignore the previous one and the lambda thing, was in muppet mode and didn&#8221;t test things.</p>
<p><code>import win32com<br />
import time</code></p>
<p><code>def simpleAddition(foo):<br />
&nbsp;&nbsp;&nbsp;&nbsp;foo = foo +1<br />
&nbsp;&nbsp;&nbsp;&nbsp;return foo</code></p>
<p><code>#Application.CreatePrim("Sphere", "MeshSurface", "", "")<br />
#Application.SetValue("sphere.polymsh.geom.subdivu", 300, "")<br />
#Application.SetValue("sphere.polymsh.geom.subdivv", 300, "")</code></p>
<p><code>overallStartTime = time.clock()</code></p>
<p><code>tupPointsPosition = Application.Selection(0).ActivePrimitive.Geometry.Points.PositionArray</code></p>
<p><code>startTime = time.clock()<br />
forRangeAppendPointsPosition = [[],[],[]]<br />
for i in range(len(tupPointsPosition[0])):<br />
&nbsp;&nbsp;&nbsp;&nbsp;forRangeAppendPointsPosition[0].append(simpleAddition(tupPointsPosition[0][i]))<br />
&nbsp;&nbsp;&nbsp;&nbsp;forRangeAppendPointsPosition[1].append(simpleAddition(tupPointsPosition[1][i]))<br />
&nbsp;&nbsp;&nbsp;&nbsp;forRangeAppendPointsPosition[2].append(simpleAddition(tupPointsPosition[2][i]))<br />
endTime = time.clock()<br />
print "for range appending the new value         " + str(endTime-startTime)<br />
testList = forRangeAppendPointsPosition<br />
if testList == forRangeAppendPointsPosition: print"check"</code></p>
<p><code>startTime = time.clock()<br />
forInPointPosition = [[],[],[]]<br />
for i in tupPointsPosition[0]:<br />
&nbsp;&nbsp;&nbsp;&nbsp;forInPointPosition[0].append(simpleAddition(i))<br />
for i in tupPointsPosition[1]:<br />
&nbsp;&nbsp;&nbsp;&nbsp;forInPointPosition[1].append(simpleAddition(i))<br />
for i in tupPointsPosition[2]:<br />
&nbsp;&nbsp;&nbsp;&nbsp;forInPointPosition[2].append(simpleAddition(i))<br />
endTime = time.clock()<br />
print "for each element and appending            " + str(endTime-startTime)<br />
if testList == forInPointPosition: print"check"</code></p>
<p><code>startTime = time.clock()<br />
forRangeInPlacePointsPosition = [list(tupPointsPosition[0]),list(tupPointsPosition[1]),list(tupPointsPosition[2])]<br />
for i in range(len(tupPointsPosition[0])):<br />
&nbsp;&nbsp;&nbsp;&nbsp;forRangeInPlacePointsPosition[0][i] = simpleAddition(tupPointsPosition[0][i])<br />
&nbsp;&nbsp;&nbsp;&nbsp;forRangeInPlacePointsPosition[1][i] = simpleAddition(tupPointsPosition[1][i])<br />
&nbsp;&nbsp;&nbsp;&nbsp;forRangeInPlacePointsPosition[2][i] = simpleAddition(tupPointsPosition[2][i])<br />
endTime = time.clock()<br />
print "for range modifying in place              " + str(endTime-startTime)<br />
if testList == forRangeInPlacePointsPosition: print"check"</code></p>
<p><code>#no for each modifying in place as retrieving the index of the value<br />
#makes modification in place unreliable and dangerous<br />
#or overly complicated and slow to make it safe</code></p>
<p><code>startTime = time.clock()<br />
mapPosX = map(simpleAddition, tupPointsPosition[0])<br />
mapPosY = map(simpleAddition, tupPointsPosition[1])<br />
mapPosZ = map(simpleAddition, tupPointsPosition[2])<br />
mapPointsPosition = [mapPosX, mapPosY, mapPosZ ]<br />
endTime = time.clock()<br />
print "mapping the function                      " + str(endTime-startTime)<br />
if testList == mapPointsPosition: print"check"</code></p>
<p><code>startTime = time.clock()<br />
mapLambdaPosX = map(lambda x: x+1, tupPointsPosition[0])<br />
mapLambdaPosY = map(lambda x: x+1, tupPointsPosition[1])<br />
mapLambdaPosZ = map(lambda x: x+1, tupPointsPosition[2])<br />
mapLambdaPointsPosition = [mapPosX, mapPosY, mapPosZ ]<br />
endTime = time.clock()<br />
print "mapping a lambda function                 " + str(endTime-startTime)<br />
if testList == mapLambdaPointsPosition: print"check"</code></p>
<p><code>startTime = time.clock()<br />
listCompPointsPosition = [[simpleAddition(i) for i in tupPointsPosition[0]],[simpleAddition(i) for i in tupPointsPosition[1]],[simpleAddition(i) for i in tupPointsPosition[2]]]<br />
endTime = time.clock()<br />
print "list comprehension                        " + str(endTime-startTime)<br />
if testList == listCompPointsPosition: print"check"</code></p>
<p><code>startTime = time.clock()<br />
listCompDirectPointsPosition = [[i+1 for i in tupPointsPosition[0]],[i+1 for i in tupPointsPosition[1]],[i+1 for i in tupPointsPosition[2]]]<br />
endTime = time.clock()<br />
print "list comprehension with embedded function " + str(endTime-startTime)<br />
if testList == listCompDirectPointsPosition: print"check"</code></p>
<p><code>overallEndTime = time.clock()<br />
print "total time elapsed:  " + str(overallEndTime-overallStartTime)</code></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Raffaele Fragapane</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-546</link>
		<dc:creator>Raffaele Fragapane</dc:creator>
		<pubDate>Wed, 14 Dec 2005 09:51:15 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-546</guid>
		<description>ok, let&#039;&#039;s drop the confrontational thing, which was never my intention to start.
I see what you&#039;&#039;re trying to get across in the article, and I never said your results were wrong.

I said the way your statements try to encompass the whole looping techniques thing is what I&#039;&#039;m arguing.

remember when I said that you should try different testbeds? and that applying mathematical operations to an array will be the fastest by mapping or comprehension and that such a situation wasn&#039;&#039;t so uncommon (it&#039;&#039;s infact more performance critical then walking selections or curves, as such topology walks are at the heart of deformers, which NEED to be fast).

in the following code, which is representative of the time required to create lists or not in each technique and how they are created, you can see the speed progression I described in my reply.
it is just one of MANY examples where performance ladders are considerably different from that in your conclusions.

the innner working of an interpreter and the various caching/loading/reloading and instancing operations needed to run them can tip the scales in considerably different ways in different situations.
IE: notice how a lambda function will make map slightly faster, but will slow down list comprehension considerably.

if the site kills the formatting, indent once lines:
5, 6, 19, 20, 21, 30, 32, 34, 43, 44, 45
I hope it helps furthering your understanding of programming and improving your scripts:


------
Editor&#039;&#039;s note:

Script removed.
Please refer to the script in &lt;a href=&quot;http://www.xsi-blog.com/?p=31#comment-547&quot;&gt;this&lt;/a&gt; comment.
------</description>
		<content:encoded><![CDATA[<p>ok, let&#8217;&#8217;s drop the confrontational thing, which was never my intention to start.<br />
I see what you&#8221;re trying to get across in the article, and I never said your results were wrong.</p>
<p>I said the way your statements try to encompass the whole looping techniques thing is what I&#8221;m arguing.</p>
<p>remember when I said that you should try different testbeds? and that applying mathematical operations to an array will be the fastest by mapping or comprehension and that such a situation wasn&#8221;t so uncommon (it&#8217;&#8217;s infact more performance critical then walking selections or curves, as such topology walks are at the heart of deformers, which NEED to be fast).</p>
<p>in the following code, which is representative of the time required to create lists or not in each technique and how they are created, you can see the speed progression I described in my reply.<br />
it is just one of MANY examples where performance ladders are considerably different from that in your conclusions.</p>
<p>the innner working of an interpreter and the various caching/loading/reloading and instancing operations needed to run them can tip the scales in considerably different ways in different situations.<br />
IE: notice how a lambda function will make map slightly faster, but will slow down list comprehension considerably.</p>
<p>if the site kills the formatting, indent once lines:<br />
5, 6, 19, 20, 21, 30, 32, 34, 43, 44, 45<br />
I hope it helps furthering your understanding of programming and improving your scripts:</p>
<p>&#8212;&#8212;<br />
Editor&#8217;&#8217;s note:</p>
<p>Script removed.<br />
Please refer to the script in <a href="http://www.xsi-blog.com/?p=31#comment-547">this</a> comment.<br />
&#8212;&#8212;</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Bernard Lebel</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-170</link>
		<dc:creator>Bernard Lebel</dc:creator>
		<pubDate>Thu, 29 Sep 2005 02:16:00 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-170</guid>
		<description></description>
		<content:encoded><![CDATA[<p>Raffaele,</p>
<p>It is with the greatest of interest that I have read your last post. Recently I had some time to tinker about it, so let&#8217;&#8217;s review your comments&#8230;</p>
<p>&gt;&gt; you first have to do it for XSI’s SDK, figuring out what objects and methods, under a constant condition (like using always the same iteration technique), offer what kind of results.</p>
<p>[Bernard] This article is about using in XSI some of the available looping techniques offered by Python, with the only exception being FindObjects(), wich I provided only for a matter of comparison. I don&#8221;t know where you got that but I have never pretended that some techniques were better or worse than some techniques offered by the XSI objects and methods (other than the FindObjects call). I voluntarily left out things like FindChildren, Filter, Find, Item, and so on. I focused entirely on Python specific looping techniques, hence the title of this article. The intention was not to cover all possible combinations of all available looping techniques, wich would probably make a book. I don&#8221;t see how this made my conclusions flawed.</p>
<p>&gt;&gt; once you’ve done that you test the same set under all the looping techniques.</p>
<p>[Bernard] This is what I have done nearly throughout the entire article, Raffaele&#8230;</p>
<p>&gt;&gt; once you’ve done that you test this second level permutation (which will be at least 16 different snippets just using 4 possible looping techniques on 4 different kinds of storing and handling data) you’ll then have to figure out what direct optimization techniques you could use and when they are beneficial or detrimental.</p>
<p>[Bernard] Okay another point here. I&#8221;m in complete agreement with you that each looping technique has to be carefully chosen depending on the context. Yes, in some cases, some types of loops will work better than others. I used only a handful of looping techniques in only a handful of contexts.</p>
<p>Still, one has to admit that even though pulling constants out of a few tests is not possible, tendencies, or should I say, outlines, can be drawn and hold on tight until there is proof of the contrary is found. As opposed to what you state I have not attempted to find stone truths, I explored few possibilities in few context. The possiblities I have explored may or may not extend beyond their context, that is up again for experimentation. Still, I believe the conclusion I have pulled from this experimentation is 100% valid and could easily apply to many different scenarios.</p>
<p>&gt;&gt; one example is map, that you insist saying isn’t much of a performance booster when dealing with functions written inside your python code. that statement could be almost correct in this one particular case, but have you tryied mapping a simple transform over a position array list?</p>
<p>[Bernard] What if I did?</p>
<p>===================================================<br />
1- Moving a keyframe in X translation by 3 units</p>
<p>In this exercice, I have created a null, animated it over 100 frames, changed the fcurves to linear, then plotted its transformations for each frame. As a result, the null had 100 keyframes on all translation axes.<br />
The script has to take the posx fcurve, and apply a simple addition of 3 units to each keyframe.</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def moveUp( oKey ):<br />
	# Append in-place the list of new keyframes<br />
	aList.append( oKey.time )<br />
	aList.append( oKey.value + nAmount )</p>
<p>def main():</p>
<p>	global aList<br />
	global nAmount</p>
<p>	# Get selected object<br />
	oSel = xsi.selection(0)</p>
<p>	# Specify the amount of transformation we want<br />
	nAmount = 3.0</p>
<p>	# Create empty list to store new array of time/value pairs<br />
	aList = []</p>
<p>	# Map keys to function<br />
	map( moveUp, oSel.posx.source.keys )</p>
<p>	# Put new keyframes onto fcurve<br />
	oSel.posx.source.setkeys( aList )</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS:<br />
#INFO :<br />
#INFO : 0.052<br />
#INFO : 0.104<br />
#INFO : 0.156<br />
#INFO : 0.209<br />
#INFO : 0.26<br />
#INFO : 0.313<br />
#INFO : 0.365<br />
#INFO : 0.417<br />
#INFO : 0.469<br />
#INFO : 0.521</p>
<p>In the next script, I just made a slight modification. Instead of putting times and values into a list and then setting keys on the fcurve, I modify the keys in-place directly. That made the code a little bit shorter, as there was no need to declare globals.</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def moveUp( oKey ):<br />
	oKey.value += 3.0</p>
<p>def main():</p>
<p>	# Get selected object<br />
	oSel = xsi.selection(0)</p>
<p>	# Map keys to function<br />
	map( moveUp, oSel.posx.source.keys )</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS:<br />
#INFO :<br />
#INFO : 0.061<br />
#INFO : 0.122<br />
#INFO : 0.183<br />
#INFO : 0.245<br />
#INFO : 0.307<br />
#INFO : 0.368<br />
#INFO : 0.429<br />
#INFO : 0.491<br />
#INFO : 0.552<br />
#INFO : 0.614</p>
<p>Now let&#8217;&#8217;s try with a classic for loop. There was no need for another function, now the loop can be done in the main function.</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def main():</p>
<p>	# Get selected object<br />
	oSel = xsi.selection(0)<br />
	oFcurve = oSel.posx.source</p>
<p>	# Specify the amount of transformation we want<br />
	nAmount = 3.0</p>
<p>	# Create empty list to store new array of time/value pairs<br />
	aList = []</p>
<p>	for oKey in oFcurve.keys:<br />
		aList.append( oKey.time )<br />
		aList.append( oKey.value + nAmount )</p>
<p>	# Put new keyframes onto fcurve<br />
	oFcurve.setkeys( aList )</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS<br />
#INFO :<br />
#INFO : 0.051<br />
#INFO : 0.103<br />
#INFO : 0.155<br />
#INFO : 0.206<br />
#INFO : 0.258<br />
#INFO : 0.309<br />
#INFO : 0.36<br />
#INFO : 0.412<br />
#INFO : 0.464<br />
#INFO : 0.515</p>
<p>Very, very slightly faster&#8230;. It&#8217;&#8217;s certainly not excluded that I have not written the best possible way the map() calls, but there are also other factors to consider. First, since the loop is done in the main function, the calls to another are suppressed. In Python, every call, as lightweight as it might be, involves a performance cost. Also, the fact that I have suppressed the need to lookup the global scope for the aList and nAmount objects might have speed up things a little, as I already explained that looking up a wider scope than the &#8220;current&#8221; one also involves a performance cost.</p>
<p>Now let&#8217;&#8217;s try a modification in-place using a classic for loop:</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def main():</p>
<p>	# Get selected object<br />
	oSel = xsi.selection(0)<br />
	oFcurve = oSel.posx.source</p>
<p>	# Specify the amount of transformation we want<br />
	nAmount = 3.0</p>
<p>	# Create empty list to store new array of time/value pairs<br />
	aList = []</p>
<p>	for oKey in oFcurve.keys:<br />
		oKey.value += nAmount</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS<br />
#INFO :<br />
#INFO : 0.06<br />
#INFO : 0.122<br />
#INFO : 0.183<br />
#INFO : 0.245<br />
#INFO : 0.307<br />
#INFO : 0.368<br />
#INFO : 0.43<br />
#INFO : 0.492<br />
#INFO : 0.554<br />
#INFO : 0.617</p>
<p>Interesting&#8230;. Compared to the map version, the for loop seems faster in the beginning, but is gradually slowing down, to the point where it is slower than the map() version. What is also interesting is the fact that the population of the list in-place was, both with map and for, rather steady compared to each other.</p>
<p>Now I wonder what would be the result of the last two scripts, those that modify keyframe values in place, if we had 10,000 keyframes instead of 100. Let see.</p>
<p>The map version:</p>
<p>#INFO :<br />
#INFO : 64.185<br />
#INFO : 128.029<br />
#INFO : 194.52<br />
#INFO : 258.883<br />
#INFO : 326.49<br />
#INFO : 392.737<br />
#INFO : 459.738<br />
#INFO : 526.23<br />
#INFO : 593.348<br />
#INFO : 660.818</p>
<p>The for version:</p>
<p>#INFO :<br />
#INFO : 61.918<br />
#INFO : 123.811<br />
#INFO : 185.709<br />
#INFO : 247.661<br />
#INFO : 309.602<br />
#INFO : 371.551<br />
#INFO : 433.595<br />
#INFO : 495.631<br />
#INFO : 557.791<br />
#INFO : 619.819</p>
<p>Hummm in this case, the map version not only did worse than the for version, but the result is not the same at all as was we had before, the speed of the for loop remains more consistantly lower than the map version.</p>
<p>===================================================<br />
2- Computing tranform multiplications</p>
<p>Very well, enough of that. I figured that I could do a different exercice. Perhaps I have not chosen the right use for the map call. Thus I have created a null that I keyframed at the origin. Then I moved it to (1000, 1000, 1000) and keyframed it. Then I changed the curves to linear interpolation. Then I duplicated from animation this null, I ended up with 1000 nulls. In this exercice I extract the transformations of the nulls, multiply them by another transform, and set these transforms back on the nulls.</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application<br />
XSIMath = win32com.client.Dispatch( &#8221;XSI.Math&#8221; )</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		#xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def moveDown( oObject ):<br />
	# Get current object transforms<br />
	oObjTransform = oObject.Kinematics.Local.Transform</p>
<p>	# Create new transformation<br />
	oNewTransform = XSIMath.CreateTransform()</p>
<p>	# Multiply object transform by global transform<br />
	oNewTransform.Mul( oObjTransform, oTransform )</p>
<p>	oObject.Kinematics.Local.Transform = oNewTransform</p>
<p>def main():</p>
<p>	global oTransform<br />
	oTransform = XSIMath.CreateTransform()<br />
	oTransform.SetTranslationFromValues( -1.578, 3.45987, 4.23490 )</p>
<p>	map( moveDown, xsi.selection )</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS<br />
#INFO :<br />
#INFO : 2.916<br />
#INFO : 5.857<br />
#INFO : 8.809<br />
#INFO : 11.765<br />
#INFO : 14.708<br />
#INFO : 17.669<br />
#INFO : 20.625<br />
#INFO : 23.542<br />
#INFO : 26.487<br />
#INFO : 29.444</p>
<p>Then with a for loop version:</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application<br />
XSIMath = win32com.client.Dispatch( &#8221;XSI.Math&#8221; )</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def main():</p>
<p>	oTransform = XSIMath.CreateTransform()<br />
	oTransform.SetTranslationFromValues( -1.578, 3.45987, 4.23490 )</p>
<p>	for oObject in xsi.selection:<br />
		# Get current object transforms<br />
		oObjTransform = oObject.Kinematics.Local.Transform</p>
<p>		# Create new transformation<br />
		oNewTransform = XSIMath.CreateTransform()</p>
<p>		# Multiply object transform by global transform<br />
		oNewTransform.Mul( oObjTransform, oTransform )</p>
<p>		oObject.Kinematics.Local.Transform = oNewTransform</p>
<p># Call timer<br />
timer()</p>
<p>#INFO :<br />
#INFO : 2.912<br />
#INFO : 5.876<br />
#INFO : 8.869<br />
#INFO : 11.851<br />
#INFO : 14.87<br />
#INFO : 17.857<br />
#INFO : 20.848<br />
#INFO : 23.821<br />
#INFO : 26.813<br />
#INFO : 29.786</p>
<p>Mmmm not much differences, all in all the for is slower by a fraction. Not very conclusive. Let&#8217;&#8217;s make some modifications to the scripts. This time, instead of creating local transform objects in every iteration, we&#8221;ll create a reusable transform object. I&#8221;m not sure it will speed up or slow things down, but it worth a try, no?</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application<br />
XSIMath = win32com.client.Dispatch( &#8221;XSI.Math&#8221; )</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def moveDown( oObject ):<br />
	# Get current object transforms<br />
	oObjTransform = oObject.Kinematics.Local.Transform</p>
<p>	# Multiply object transform by global transform<br />
	oNewTransform.Mul( oObjTransform, oTransform )</p>
<p>	oObject.Kinematics.Local.Transform = oNewTransform</p>
<p>def main():</p>
<p>	global oTransform<br />
	oTransform = XSIMath.CreateTransform()<br />
	oTransform.SetTranslationFromValues( -1.578, 3.45987, 4.23490 )</p>
<p>	global oNewTransform<br />
	oNewTransform = XSIMath.CreateTransform()</p>
<p>	map( moveDown, xsi.selection )</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS<br />
#INFO :<br />
#INFO : 2.186<br />
#INFO : 4.45<br />
#INFO : 6.734<br />
#INFO : 8.993<br />
#INFO : 11.249<br />
#INFO : 13.496<br />
#INFO : 15.743<br />
#INFO : 18.012<br />
#INFO : 20.298<br />
#INFO : 22.537</p>
<p>Nice! Just using a global object instead of recreating it at each iteration made things faster. Now let see how things are going with the for loop.</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application<br />
XSIMath = win32com.client.Dispatch( &#8221;XSI.Math&#8221; )</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def main():</p>
<p>	oTransform = XSIMath.CreateTransform()<br />
	oTransform.SetTranslationFromValues( -1.578, 3.45987, 4.23490 )</p>
<p>	# Create new transformation<br />
	oNewTransform = XSIMath.CreateTransform()</p>
<p>	for oObject in xsi.selection:<br />
		# Get current object transforms<br />
		oObjTransform = oObject.Kinematics.Local.Transform</p>
<p>		# Multiply object transform by global transform<br />
		oNewTransform.Mul( oObjTransform, oTransform )</p>
<p>		oObject.Kinematics.Local.Transform = oNewTransform</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS<br />
#INFO :<br />
#INFO : 2.224<br />
#INFO : 4.492<br />
#INFO : 6.705<br />
#INFO : 8.975<br />
#INFO : 11.209<br />
#INFO : 13.437<br />
#INFO : 15.658<br />
#INFO : 17.871<br />
#INFO : 20.095<br />
#INFO : 22.351</p>
<p>The for loop starts a little slower, but catches and even surpasses the map loop.</p>
<p>Okay, time to pull out some conclusions. First, there are multiple other uses of loops. These tests were just another attempt at measuring differences in speed in a given context. The difference were generally very minor, by a magnitude of 100ths of a second for the &#8220;quick&#8221; loops, and by an order of 3 to 40 seconds for the longer loops. In some cases the map performed better, in other case the for loop performed better. Only in the longer loops did the for really provided a big difference. So based on this new series of tests, I still can&#8221;t change my mind about the fact that a for loop is faster than a map when used with Python functions.</p>
<p>===================================================<br />
3- Other forms of loop</p>
<p>But wait! I remembered that lambdas and list comprehensions were mentioned. Lambdas are still a little bit obscure to me, but I decided to give it a go, hoping I will not mess things. Let see&#8230;.</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def main():</p>
<p>	# Get selected object<br />
	oSel = xsi.selection(0)<br />
	oFcurve = oSel.posx.source</p>
<p>	# Specify the amount of transformation we want<br />
	nAmount = 3.0</p>
<p>	# Map to lambda function, returns tuples<br />
	aTuples = map( ( lambda oKey: ( oKey.time, oKey.value + nAmount ) ), oFcurve.keys )</p>
<p>	# Flatten list<br />
	aFlatList = [ nValue for tTuple in aTuples for nValue in tTuple ]</p>
<p>	# Put new keyframes onto fcurve<br />
	oFcurve.setkeys( aFlatList )</p>
<p># Call timer<br />
timer()</p>
<p># RESULTS<br />
#INFO :<br />
#INFO : 0.052<br />
#INFO : 0.104<br />
#INFO : 0.156<br />
#INFO : 0.207<br />
#INFO : 0.259<br />
#INFO : 0.31<br />
#INFO : 0.362<br />
#INFO : 0.414<br />
#INFO : 0.465<br />
#INFO : 0.517</p>
<p>I suspect that I might have not the most perfect use of the lambda (like, I don&#8221;t know if it&#8217;&#8217;s possible to return multiple values without a tuple), but still it is a perfectly legal use of lambda to use it in a map call. Possibly the list comprehension right after the map/lambda that flattens the values may involve a performance hit that nullifies the use of the map/lambda combination.</p>
<p>Now let&#8217;&#8217;s try with a list comprehension version&#8230;</p>
<p>import win32com<br />
import time</p>
<p>c = win32com.client.constants<br />
xsi = Application</p>
<p>def timer():</p>
<p>	xsi.logmessage( &#8221;&#8221; )</p>
<p>	# Record current time<br />
	time1 = time.clock()</p>
<p>	# Start loop to perform single function calls<br />
	for i in range( 0, 10 ):</p>
<p>		# Call function<br />
		main()</p>
<p>		# Record new time<br />
		time2 = time.clock()</p>
<p>		# Print execution time, rounded to 3 miliseconds<br />
		xsi.logmessage( round( time2 &#8211; time1, 3 ) )</p>
<p>def main():</p>
<p>	# Get selected object<br />
	oSel = xsi.selection(0)<br />
	oFcurve = oSel.posx.source</p>
<p>	# Specify the amount of transformation we want<br />
	nAmount = 3.0</p>
<p>	# Get pairs of new time/value<br />
	aTuples = [ ( oKey.time, oKey.value + nAmount ) for oKey in oFcurve.keys ]</p>
<p>	# Flatten list<br />
	aFlatList = [ nValue for tTuple in aTuples for nValue in tTuple ]</p>
<p>	# Put new keyframes onto fcurve<br />
	oFcurve.setkeys( aFlatList )</p>
<p># Call timer<br />
timer()</p>
<p># RESULT<br />
#INFO :<br />
#INFO : 0.051<br />
#INFO : 0.104<br />
#INFO : 0.156<br />
#INFO : 0.208<br />
#INFO : 0.26<br />
#INFO : 0.311<br />
#INFO : 0.363<br />
#INFO : 0.414<br />
#INFO : 0.465<br />
#INFO : 0.516</p>
<p>Again, extremely minimal difference&#8230;.</p>
<p>===================================================<br />
4- Summary of tests</p>
<p>Okay so let&#8217;&#8217;s put all the result tables next to each other, so we can have a better idea&#8230;.</p>
<p>#INFO :<br />
#INFO : 0.052<br />
#INFO : 0.104<br />
#INFO : 0.156<br />
#INFO : 0.209<br />
#INFO : 0.26<br />
#INFO : 0.313<br />
#INFO : 0.365<br />
#INFO : 0.417<br />
#INFO : 0.469<br />
#INFO : 0.521</p>
<p>#INFO :<br />
#INFO : 0.051<br />
#INFO : 0.103<br />
#INFO : 0.155<br />
#INFO : 0.206<br />
#INFO : 0.258<br />
#INFO : 0.309<br />
#INFO : 0.36<br />
#INFO : 0.412<br />
#INFO : 0.464<br />
#INFO : 0.515</p>
<p>#INFO :<br />
#INFO : 0.052<br />
#INFO : 0.104<br />
#INFO : 0.156<br />
#INFO : 0.207<br />
#INFO : 0.259<br />
#INFO : 0.31<br />
#INFO : 0.362<br />
#INFO : 0.414<br />
#INFO : 0.465<br />
#INFO : 0.517</p>
<p>#INFO :<br />
#INFO : 0.051<br />
#INFO : 0.104<br />
#INFO : 0.156<br />
#INFO : 0.208<br />
#INFO : 0.26<br />
#INFO : 0.311<br />
#INFO : 0.363<br />
#INFO : 0.414<br />
#INFO : 0.465<br />
#INFO : 0.516</p>
<p># 100 keyframes<br />
#INFO :<br />
#INFO : 0.061<br />
#INFO : 0.122<br />
#INFO : 0.183<br />
#INFO : 0.245<br />
#INFO : 0.307<br />
#INFO : 0.368<br />
#INFO : 0.429<br />
#INFO : 0.491<br />
#INFO : 0.552<br />
#INFO : 0.614</p>
<p>#INFO :<br />
#INFO : 0.06<br />
#INFO : 0.122<br />
#INFO : 0.183<br />
#INFO : 0.245<br />
#INFO : 0.307<br />
#INFO : 0.368<br />
#INFO : 0.43<br />
#INFO : 0.492<br />
#INFO : 0.554<br />
#INFO : 0.617</p>
<p># 10,000 keyframes<br />
#INFO :<br />
#INFO : 64.185<br />
#INFO : 128.029<br />
#INFO : 194.52<br />
#INFO : 258.883<br />
#INFO : 326.49<br />
#INFO : 392.737<br />
#INFO : 459.738<br />
#INFO : 526.23<br />
#INFO : 593.348<br />
#INFO : 660.818</p>
<p>#INFO :<br />
#INFO : 61.918<br />
#INFO : 123.811<br />
#INFO : 185.709<br />
#INFO : 247.661<br />
#INFO : 309.602<br />
#INFO : 371.551<br />
#INFO : 433.595<br />
#INFO : 495.631<br />
#INFO : 557.791<br />
#INFO : 619.819</p>
<p>#INFO :<br />
#INFO : 2.916<br />
#INFO : 5.857<br />
#INFO : 8.809<br />
#INFO : 11.765<br />
#INFO : 14.708<br />
#INFO : 17.669<br />
#INFO : 20.625<br />
#INFO : 23.542<br />
#INFO : 26.487<br />
#INFO : 29.444</p>
<p>#INFO :<br />
#INFO : 2.912<br />
#INFO : 5.876<br />
#INFO : 8.869<br />
#INFO : 11.851<br />
#INFO : 14.87<br />
#INFO : 17.857<br />
#INFO : 20.848<br />
#INFO : 23.821<br />
#INFO : 26.813<br />
#INFO : 29.786</p>
<p>#INFO :<br />
#INFO : 2.186<br />
#INFO : 4.45<br />
#INFO : 6.734<br />
#INFO : 8.993<br />
#INFO : 11.249<br />
#INFO : 13.496<br />
#INFO : 15.743<br />
#INFO : 18.012<br />
#INFO : 20.298<br />
#INFO : 22.537</p>
<p>#INFO :<br />
#INFO : 2.224<br />
#INFO : 4.492<br />
#INFO : 6.705<br />
#INFO : 8.975<br />
#INFO : 11.209<br />
#INFO : 13.437<br />
#INFO : 15.658<br />
#INFO : 17.871<br />
#INFO : 20.095<br />
#INFO : 22.351</p>
<p>To conclude this series of tests, I will say that the use I made of map, for, lambda and list comprehension did NOT invalidated any of the conclusion from the top article, and I have yet to find a scenario where using map will be faster than for loops with Python functions. At this point, if you are in disagreement with me, I think it would be a better solution for you to provide some code. Unless you provide evidence of your statements, I&#8221;m affraid I&#8221;ll have to stay on my positions on that one. I think I have done my homeworks.</p>
<p>Btw, all scripts I have wrote for this post are available here:<br />
<a href="http://www.bernardlebel.com/scripts/xsi/scripts/others/xsiblog/pythonloopingtechniques/" rel="nofollow">http://www.bernardlebel.com/scripts/xsi/scripts/others/xsiblog/pythonloopingtechniques/</a></p>
<p>&gt;&gt; when you use a single test bed for your performance analisys, and one that involves such a highly SDK dependant thing like XSI’s internal collections, you’re totally voiding your efforts.</p>
<p>[Bernard] With all due respect to your experience and knowledge, I&#8221;m in largely in disagreement here. Again, this article is all about using Python looping techniques in XSI, *nothing more*. The code runs in XSI at all time, in the same build, same machine, same OS, same Python version, well same conditions at all times. In this article I have questioned Python *in XSI*, period. Of course it&#8217;&#8217;s dependant of the SDK. So? That means we should try to benchmark Python&#8217;&#8217;s performance? Of course the results may be different than running similar code in another environment. But I did not talked about that.</p>
<p>One last thing&#8230;</p>
<p>&gt;&gt; hopefully now you understand what I meant when I said I find the article flawed in its approach and in the adamantine quality some of the statements have in them.</p>
<p>I take what I do very seriously, and a considerable amount of time has been spent on these posts. Therefore, I consider that it would be the most elementary courtesy and respect from you, if you wish to further refute my statements, to show a similar commitment and provide tangible material to support your statements. I think I have gone far enough with this for you to go past the commenting approach and take an educating one.</p>
<p>I know you are a knowledgeable guy and I respect that, and would be grateful if you could teach me something; I&#8221;m happy to revisit my positions, as long as we&#8221;re on the same level of seriousness. Still I thank you for your comments, they made me learn a few things already.</p>
<p>Bernard</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Raffaele Fragapane</title>
		<link>http://www.softimageblog.com/archives/31/comment-page-1#comment-111</link>
		<dc:creator>Raffaele Fragapane</dc:creator>
		<pubDate>Thu, 08 Sep 2005 04:43:19 +0000</pubDate>
		<guid isPermaLink="false">http://www.xsi-blog.com/?p=31#comment-111</guid>
		<description>lemme refine my statements a bit then.

what you deal with in this article is clearly oriented to performance tuning.
now, performance tuning is a VERY task specific issue to deal with, no 2 pieces of code, even when the apparent differences are minimal, benefit in the same way from the same exact tuning in the same way.

performance is dictated by several elements working in sinergy. part of it is the language you are using or, more specifically, the backend to that language and how it deals with different programming paradigms and the various and different tools that it offers to handle a generic dataset in substantially different ways. however, a HUGE part of performance issues could very well spawn from something entirely different, like the API and the SDK you are using, the way such tools handle calls marshalling and internal automation/optimization of processes and all that.

when you use a single test bed for your performance analisys, and one that involves such a highly SDK dependant thing like XSI&#039;&#039;s internal collections, you&#039;&#039;re totally voiding your efforts.

one example is map, that you insist saying isn&#039;&#039;t much of a performance booster when dealing with functions written inside your python code. that statement could be almost correct in this one particular case, but have you tryied mapping a simple transform over a position array list?

on something simple, like getting the position array, decomposing it and adding a fixed amount to one axis of the position, and then dumping it back into the geometry, mapping that function VS looping it in a range provides a tenfold difference in performance, and that is because what you do is creating the list, manipulating it all in one go, and then dump it back.
further then that, in such a simple case you could achieve the same result, without obfuscating the code, and inlining the operation with a lambda, and that would give you another small, but nearly irrelevant boost.
last but not least, the same thing, with list comprehension, will probably result in something even faster, if only by 10-20%, then map.

now, isn&#039;&#039;t that a case that happens VERY often when writing for XSI? manipulating an array to alter geometry I mean, and isn&#039;&#039;t that giving results radically different from what you are stating as a fairly cold hard truth here?

trying to generalize the subject of performance tuning, even in such a simple environment like an interpret language in one single application, is still damn hard.
there&#039;&#039;s hundreds of pages in huge very technical books that deal with optimization in a single language in a single API in a single environment (like optimizing terrain generation in C + OGL), and yet they barely scratch the surface.

drawing conclusions about all the possibilities in python from one single test is misleading to say the least.

if you want to decompose and chart performance optimization, you first have to do it for XSI&#039;&#039;s SDK, figuring out what objects and methods, under a constant condition (like using always the same iteration technique), offer what kind of results.
once you&#039;&#039;ve done that you test the same set under all the looping techniques.
once you&#039;&#039;ve done that you test this second level permutation (which will be at least 16 different snippets just using 4 possible looping techniques on 4 different kinds of storing and handling data) you&#039;&#039;ll then have to figure out what direct optimization techniques you could use and when they are beneficial or detrimental.

like sometimes committing a collection to a different datatype, handling it, and committing it back into a collection (or parsing the dataset) will provide obvious boosts, because the translation overhead is only a small loss compare to the speed boost gained from a language&#039;&#039;s tricks you could use.
some other times that overhead will negate the benefits and then slow you down some.

considering 4 or 5 different tricks and techniques of committing or dereferencing data, your 3rd level permutation would be already 80 different situations to test and chart, and that is leaving out all the shades of gray inbetween.

hopefully now you understand what I meant when I said I find the article flawed in its approach and in the adamantine quality some of the statements have in them.</description>
		<content:encoded><![CDATA[<p>lemme refine my statements a bit then.</p>
<p>what you deal with in this article is clearly oriented to performance tuning.<br />
now, performance tuning is a VERY task specific issue to deal with, no 2 pieces of code, even when the apparent differences are minimal, benefit in the same way from the same exact tuning in the same way.</p>
<p>performance is dictated by several elements working in sinergy. part of it is the language you are using or, more specifically, the backend to that language and how it deals with different programming paradigms and the various and different tools that it offers to handle a generic dataset in substantially different ways. however, a HUGE part of performance issues could very well spawn from something entirely different, like the API and the SDK you are using, the way such tools handle calls marshalling and internal automation/optimization of processes and all that.</p>
<p>when you use a single test bed for your performance analisys, and one that involves such a highly SDK dependant thing like XSI&#8217;&#8217;s internal collections, you&#8221;re totally voiding your efforts.</p>
<p>one example is map, that you insist saying isn&#8221;t much of a performance booster when dealing with functions written inside your python code. that statement could be almost correct in this one particular case, but have you tryied mapping a simple transform over a position array list?</p>
<p>on something simple, like getting the position array, decomposing it and adding a fixed amount to one axis of the position, and then dumping it back into the geometry, mapping that function VS looping it in a range provides a tenfold difference in performance, and that is because what you do is creating the list, manipulating it all in one go, and then dump it back.<br />
further then that, in such a simple case you could achieve the same result, without obfuscating the code, and inlining the operation with a lambda, and that would give you another small, but nearly irrelevant boost.<br />
last but not least, the same thing, with list comprehension, will probably result in something even faster, if only by 10-20%, then map.</p>
<p>now, isn&#8221;t that a case that happens VERY often when writing for XSI? manipulating an array to alter geometry I mean, and isn&#8221;t that giving results radically different from what you are stating as a fairly cold hard truth here?</p>
<p>trying to generalize the subject of performance tuning, even in such a simple environment like an interpret language in one single application, is still damn hard.<br />
there&#8217;&#8217;s hundreds of pages in huge very technical books that deal with optimization in a single language in a single API in a single environment (like optimizing terrain generation in C + OGL), and yet they barely scratch the surface.</p>
<p>drawing conclusions about all the possibilities in python from one single test is misleading to say the least.</p>
<p>if you want to decompose and chart performance optimization, you first have to do it for XSI&#8217;&#8217;s SDK, figuring out what objects and methods, under a constant condition (like using always the same iteration technique), offer what kind of results.<br />
once you&#8221;ve done that you test the same set under all the looping techniques.<br />
once you&#8221;ve done that you test this second level permutation (which will be at least 16 different snippets just using 4 possible looping techniques on 4 different kinds of storing and handling data) you&#8221;ll then have to figure out what direct optimization techniques you could use and when they are beneficial or detrimental.</p>
<p>like sometimes committing a collection to a different datatype, handling it, and committing it back into a collection (or parsing the dataset) will provide obvious boosts, because the translation overhead is only a small loss compare to the speed boost gained from a language&#8217;&#8217;s tricks you could use.<br />
some other times that overhead will negate the benefits and then slow you down some.</p>
<p>considering 4 or 5 different tricks and techniques of committing or dereferencing data, your 3rd level permutation would be already 80 different situations to test and chart, and that is leaving out all the shades of gray inbetween.</p>
<p>hopefully now you understand what I meant when I said I find the article flawed in its approach and in the adamantine quality some of the statements have in them.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
