Discussion:
[python-win32] speed up win32com.client
DANIEL POSE
13 years ago
Permalink
Hello,

I am writing code to program AutoCAD access from Python using pywin32. When
I need to work with a high number of AutoCAD objects, it is faster to use
vba than python.
Are there some way to speed up python code in order to work faster with
AutoCAD elements?
For example in the next code when I work with 512 AutoCAD blocks:

import win32com.client

import time

t1=time.clock()

acad= win32com.client.Dispatch("AutoCAD.Application")

doc = acad.ActiveDocument

seleccion=doc.SelectionSets.Add('selection6')

seleccion.SelectOnScreen()

t2=time.clock()

M=[]

for objeto in seleccion:

if objeto.ObjectName=='AcDbBlockReference':

M.append(objeto.InsertionPoint)

t3=time.clock()

print 'M_dimension=',len(M)

R=[]

for m in M:

for x in M:

R.append(max(m)+max(x))

print 'R_dimension=',len(R)

t4=time.clock()

t_block1=t2-t1

t_block2=t3-t2

t_block3=t4-t3

print 't_block1=',t_block1

print 't_block2=',t_block2

print 't_block3=',t_block3


The output for the code is the following:

M_dimension= 512
R_dimension= 262144
t_block1= 4.25343304805
t_block2= 3.88635510938
t_block3= 0.487477319045


Then it is faster to work with R than M, even though R is bigger.

Some suggestions for speed up pywin32 code in this example?


Best Regards,

Daniel Pose.
Tim Roberts
13 years ago
Permalink
Post by DANIEL POSE
Hello,
I am writing code to program AutoCAD access from Python using pywin32.
When I need to work with a high number of AutoCAD objects, it is
faster to use vba than python.
Are there some way to speed up python code in order to work faster
with AutoCAD elements?
...
M_dimension= 512
R_dimension= 262144
t_block1= 4.25343304805
t_block2= 3.88635510938
t_block3= 0.487477319045
Then it is faster to work with R than M, even though R is bigger.
I hope that's not a surprise to you. The first loop (block2) involves
two calls into the AutoCAD COM object in each iteration. The second
loop (block3) is simply manipulating lists of integers, entirely within
Python.
Post by DANIEL POSE
Some suggestions for speed up pywin32 code in this example?
There isn't really anything here to speed up. You're just "glue". The
work is being done in AutoCAD. It is almost impossible for me to
believe that Visual Basic does this same loop any faster.

However, you can certainly try switching to early binding by using:
acad= win32com.client.gencache.EnsureDispatch("AutoCAD.Application")
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Tom
13 years ago
Permalink
Sorry to interject here but I have a question along the same vein:
If I have a script (in this case that interacts with Word through
win32com.client) with statements like this:
doc.ActiveWindow.Selection.BoldRun()
doc.ActiveWindow.Selection.TypeText(_type)
doc.ActiveWindow.Selection.BoldRun()

Does each line require 3 COM calls, one for ActiveWindow, one for Selection
and one for the method being called, or is the Selection object cached
somewhere?
The question really is the code below more efficient than the code above in
terms of COM calls, or is the difference minimal?
selec = doc.ActiveWindow.Selection
selec.BoldRun()
selec.TypeText(_type)
selec.BoldRun()

Its something I have always wondered. Thanks!
...
Mark Hammond
13 years ago
Permalink
Post by Tom
If I have a script (in this case that interacts with Word through
doc.ActiveWindow.Selection.BoldRun()
doc.ActiveWindow.Selection.TypeText(_type)
doc.ActiveWindow.Selection.BoldRun()
Does each line require 3 COM calls, one for ActiveWindow, one for
Selection and one for the method being called, or is the Selection
object cached somewhere?
It requires 3 COM calls - there is no caching (and your code above is a
good example of why - the "ActiveWindow" could easily change between the
value being cached and it being reused).
Post by Tom
The question really is the code below more efficient than the code above
in terms of COM calls, or is the difference minimal?
selec = doc.ActiveWindow.Selection
selec.BoldRun()
selec.TypeText(_type)
selec.BoldRun()
The above would be more efficient.

Cheers,

Mark
...
DANIEL POSE
13 years ago
Permalink
I have read about Early Binding but I hadn't tested the line that you
reference:
acad= win32com.client.gencache.EnsureDispatch("AutoCAD.Application")

For my surprise, if I replace line:

acad= win32com.client.Dispatch("AutoCAD.Application")

for the line:

acad= win32com.client.gencache.EnsureDispatch("AutoCAD.Application")

I have an error about a missing attribute in the line:

M.append(objeto.InsertionPoint)

The problem here is that if now I go back to use the line:

acad= win32com.client.Dispatch("AutoCAD.Application")

Now pywin32 brokes and I have always the same error in line:
M.append(objeto.InsertionPoint)

I need to reinstall pywin32 in order to repair the problem.
...
Mark Hammond
13 years ago
Permalink
Post by DANIEL POSE
I have read about Early Binding but I hadn't tested the line that you
acad= win32com.client.gencache.EnsureDispatch("AutoCAD.Application")
acad= win32com.client.Dispatch("AutoCAD.Application")
acad= win32com.client.gencache.EnsureDispatch("AutoCAD.Application")
M.append(objeto.InsertionPoint)
Once you use early-binding, the object becomes case sensitive where it
usually wasn't before. So I suspect you wanted something like
"insertionPoint" or some other difference in case.
Post by DANIEL POSE
acad= win32com.client.Dispatch("AutoCAD.Application")
M.append(objeto.InsertionPoint)
I need to reinstall pywin32 in order to repair the problem.
Yeah, this is unfortunate - once you've generated the early binding
support, win32com.client.Dispatch will generally be able to use it. A
work around without reinstalling would have been to delete the
win32com\client\gen_py directory.

HTH,

Mark
...
DANIEL POSE
13 years ago
Permalink
I had tried to change attribute name in several ways (InsertionPoint,
insertionPoint, insertionpoint,...) but I obtained the same error:

Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
File
"C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\startup.py",
line 128, in runfile
execfile(filename, glbs)
File "C:\Documents and Settings\Usuario\Mis
documentos\Dropbox\PYTHON\PruebaAutoCAD.py", line 29, in <module>
M.append(objeto.insertionpoint)
File "C:\Python27\lib\site-packages\win32com\client\__init__.py", line
465, in __getattr__
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self),
attr))
AttributeError: '<win32com.gen_py.AutoCAD 2008 Type Library.IAcadEntity
instance at 0x88940672>' object has no attribute 'insertionpoint'

If I delete only the win32com\client\gen_py folder It doesn't work. I
need delete for example win32com and win32 folders and restore older ones.
...
Mark Hammond
13 years ago
Permalink
...
In the gen_py directory you should find a generated file supporting the
AutoCAD object - it will have a GUID in its name so it might not be
obvious which one applies, but inside that you should find the
'IAcadEntity' object. That should have a _prop_map_get attribute which
lists the attributes available on the object - I'd expect to find
insertionPoint listed there. It may turn out it is actually on a
different object, in which case the win32com.client.CastTo() function
might be useful to get the appropriate interface.
Post by DANIEL POSE
If I delete only the win32com\client\gen_py folder It doesn't work. I
need delete for example win32com and win32 folders and restore older ones.
Hrm - I certainly can't explain that! Or maybe I can - check your
%TEMP% folder and see if there is a gen_py directory there?

Mark
DANIEL POSE
13 years ago
Permalink
Thank you Mark, I will try to use win32com.client.CastTo() because
InsertionPoint attribute is not inside IAcadEntity.

Opss, you are right, the problem was TEMP\gen_py folder. Thank you!
...
DANIEL POSE
13 years ago
Permalink
Just for conclude the discusion, the solution is win32.client.CastTo() as
Mark said. However, this modification doesn't speed up the code (at least
in my case).
Then, perhaps I need to learn C++ in order to embed python inside AutoCAD
as Dan Glassman said. But, not now...
Here I post the code using CastTo:

<code>

import win32com.client

import time

import string

import random


def nombrealeatorio(size=10, chars=string.ascii_uppercase + string.digits):

'''Esta función crea un nombre aleatorio de 10 caracteres'''

return ''.join(random.choice(chars) for x in range(size))

t1=time.clock()


acad= win32com.client.gencache.EnsureDispatch("AutoCAD.Application")

doc=win32com.client.CastTo(acad,"IAcadApplication")

doc = acad.ActiveDocument

seleccion=win32com.client.CastTo(doc,"IAcadSelectionSets")

seleccion=doc.SelectionSets.Add(nombrealeatorio())

seleccion=win32com.client.CastTo(seleccion,"IAcadSelectionSet")

seleccion.SelectOnScreen()


t2=time.clock()

M=[]

for objeto in seleccion:

objeto=win32com.client.CastTo(objeto, "IAcadBlockReference")

if objeto.ObjectName=='AcDbBlockReference':

M.append(objeto.InsertionPoint)

t3=time.clock()

print 'M_dimension=',len(M)

R=[]

for m in M:

for x in M:

R.append(max(m)+max(x))

print 'R_dimension=',len(R)

t4=time.clock()

t_block1=t2-t1

t_block2=t3-t2

t_block3=t4-t3

print 't_block1=',t_block1

print 't_block2=',t_block2

print 't_block3=',t_block3

print 't_total=',t4-t1


</code>


timing using early-binding:

M_dimension= 512

R_dimension= 262144

t_block1= 3.46952811042

t_block2= 4.81084020455

t_block3= 0.49030086226

t_total= 8.77066917723


timing using late-binding:

M_dimension= 512

R_dimension= 262144

t_block1= 3.20033803179

t_block2= 4.96431445896

t_block3= 0.570101227949

t_total= 8.7347537187


Thank you for this wonderfull mailing list and pywin32.
...
DANIEL POSE
13 years ago
Permalink
Sorry!!! my mistake!!! really there are a speed up, the timing using
late-binding is:
M_dimension= 512
R_dimension= 262144
t_block1= 3.00059867944
t_block2= 12.2370913063
t_block3= 0.488927223991
t_total= 15.7266172097

Then it is about half time using early-binding for my example. It is
wonderfull!!
...
Dan Glassman
13 years ago
Permalink
VBA is faster because it's running in-process inside AutoCAD (everything is
loaded into acad.exe). Python runs out-of-process (AutoCAD objects need to
be passed back and forth between acad.exe and python.exe).

You can use AutoCAD's ObjectARX API to embed python inside AutoCAD and make
python in-process; you'll get a significant speedup. You'll need to know
C++ in order to do that. Let me know if you'd like further details.

64-bit AutoCAD runs VBA out-of-process because there is no 64-bit VBA;
you'd see the same slowness there that you're seeing with python.

-drg
...
DANIEL POSE
13 years ago
Permalink
Thank you Dan, by now it is clear for me. Unfortunately I haven't C++
knowledge but don't worry, for me it is enought to know that the problem is
not in python or in my code.
Now I am occasionaly programmer in Python or VBA, then it would be too
complicated for me to learn C++, but perhaps I will consider this question
in the future.
...
Continue reading on narkive:
Loading...