Python performance with Blender operators In my script, I have for loop over many cube objects (~1000) and the treatment is very slow. Looking more in details, I notice that in the same amount of loops:


if I use python operation or simple blender operation like

obj = bpy.data.objects[obj_name]


or 

obj.select = True


it takes less than 0.08s (for all 1000 objects)
but as soon as I start using blender operators like:

bpy.ops.object.select_pattern() # or
bpy.ops.object.duplicate() # or
bpy.ops.object.location_clear() # or
bpy.ops.object.transform_apply() # etc.


then, the performance drops down deeply with more than 6s for the same amount of objects.


And, a last information, if I reduce the amount of objects from 1000 to 50, the same set of operations with bpy.ops is 0.03s - so extrapolating, it would be 0.6s for the 1000 objects and not 6s. It is as is with 1000 objects, we loose a factor 10 in speed in comparison than with 50 objects.

I tried reducing the complexity of my mesh changing the cubes in planes but it had no effect at whole on performance.

Is there something particular to know to improve these performance or a way to use the bpy.ops methods with better performances? Obviously, I am missing something important probably.
Most operators cause implicit scene updates. It means that every object in the scene is checked and updated if necessary. If you add e.g. mesh primitives using bpy.ops.mesh.primitive_cube_add() in a loop, every iteration creates a new cube, starts a scene update and Blender iterates over all objects in the scene and updates objects if it has to.
If you start with 0 objects, there will be 1 in the first iteration and 1 object needs to be checked in the scene update. In the second iteration, there will be 2 objects and 2 be checked. The first object was checked in the first iteration already (thus, 3 object updates in total). In the third iteration, there will be 3 objects and 3 + 2 + 1 = 6 objects checked in total. In iteration 1000, there will be 1000 objects and 500,500 checks have been carried out. Here's the formula where n is the number of objects:
$\displaystyle \sum_{n=1}^n i = 1 + 2 + ... +  n = \frac {n (n + 1)} {2} $
As you see, the runtime isn't linear and could only be if there was only one update for every object after all have been added. You need to use the &quot;low-level&quot; API - RNA methods and attributes - instead of operators to achieve better runtimes. A scene update needs to be called manually like bpy.context.scene.update() with this approach.
Many, but not all operator calls can be somehow replaced by &quot;low-level&quot; code. You can duplicate objects very efficiently like:
import bpy
from mathutils import Vector

ob = bpy.context.object
obs = []
sce = bpy.context.scene

for i in range(-48, 48, 3):
    for j in range(-48, 48, 3):
        copy = ob.copy()
        copy.location += Vector((i, j, 0))
        copy.data = copy.data.copy() # also duplicate mesh, remove for linked duplicate
        obs.append(copy)

for ob in obs:
    sce.objects.link(ob)

sce.update() # don't place this in either of the above loops!

A good comparison between 4 different ways to do the same thing:

bpy.ops.anim.keyframe_insert_menu() - don't ever use this in a script, it is solely to show a menu for the user

bpy.ops.anim.keyframe_insert() - this is supposed to be used via the UI, not in script. Use operator calls only if there is no lower-level API!

Object.keyframe_insert() - RNA method that can be called on an object, better

The low-level way - add F-Curves and keyframe_points manually, fastest but you need to do alot yourself and consider several conditions (like object not having animation_data or an animation_data.action)


Related (also examples included):

Python slowing down over time