Generating a large number of mesh primitives

Hi,

This is a problem that’s been bugging me for a while now. I work as a science type, and I use Blender to build molecular structures with a large number of atoms. However, there’s a limit to the number of UV-sphere atoms I can realistically generate, and I don’t know why.

When I run a Python script that creates a large number of meshes, ie.


import bpy, time
t0 = time.time()


for i in range(1000):
    bpy.ops.mesh.primitive_uv_sphere_add()


    if i % 100 == 0:
        print(time.time()-t0)
        t0 = time.time()

The script speed slows down exponentially


0.000012
0.446588
0.463735
0.566176
0.725833
0.999464
1.381392
1.825786
2.463431
3.281711

I can get a performance boost by duplicating an added object

bpy.ops.mesh.primitive_uv_sphere_add()
for i in range(999):
    bpy.ops.object.duplicate()

but the exponential trend is still there.

I’m on Ubuntu with 32 GB memory, so I don’t think that’s the issue. Any thoughts?

Do you get any reduction with


bpy.ops.object.duplicate(linked=True)

each object will share the same mesh as the original

operators cause scene updates, the more objects you have, the slower it becomes.

Better use low-level calls, instead of what batFINGER suggested, you can do:

import bpy

bpy.ops.object.select_all(action='DESELECT')
bpy.ops.mesh.primitive_uv_sphere_add()
sphere = bpy.context.object


for i in range(-1000, 1000, 2):
    ob = sphere.copy()
    ob.location.y = i
    #ob.data = sphere.data.copy() # uncomment this, if you want full copies and no linked duplicates
    bpy.context.scene.objects.link(ob)
bpy.context.scene.update()

Takes less then 2 seconds for me (windows 64bit, no matter if linked or full duplicates)

@batFINGER - The performance in this case seems about the same with or without the linked=True boolean. My guess is that sharing meshes reduces the memory footprint though, so it’s still very useful. Thanks!

@CoDEmanX - Wow, that worked amazingly well! That script ran in ~0.05s on my server (~0.15 for full copies). Either way, really really fast. Thank you!!

For my self-education, I’m guessing “operators” means most of the bpy.ops.* methods. These methods internally run something like bpy.context.scene.update(). Your script is fast because you don’t have any looped calls to bpy.ops.*, so that update isn’t called until you manually do so at the end. Is this correct?

Anyway, thanks again! You’ve saved me a lot of headaches :slight_smile:

Concerning python, operators are classes derived from bpy.types.Operator. After registration, these are made accessible via bpy.ops.[bl_idname]

[bl_idname] --> e.g. “mesh.select_all”

As operators are actions the user can do, they usually require a scene update / redraw after completion. But if you wanna script blender, they are really inefficient. Python scripts run exclusively, so there’s no live feedback of what’s going on (exception: modal operators). The ops cause scene updates to no purpose, as you can’t see the changes while script is still running. In addition, operators require a certain context, e.g. mouse has to be over 3D View + there’s a selected mesh in edit mode etc.

Therefore, i recommend to use low-level function calls whenever possible. Here’s an example:
http://www.blender.org/documentation/blender_python_api_2_64_9/info_quickstart.html#animation

another thin is to turn off record history

or if you can use instances may be!

salutations

Interesting. Thanks again!