Python API: Huge performance degradation in Blender 2.8 #63662

Closed
opened 2019-04-16 16:02:12 +02:00 by Marc · 11 comments

System Information
Operating system: Windows 7, 64bit
Graphics card:

Blender Version
Broken: (example: 2.80, edc1b0167518)
Worked: (optional) 2.79b

Short description of error

I'm using Blender as a 3D live view controlled from an external application (see https://developer.blender.org/T62051 for more details).

I just noticed that, compared to Blender 2.79, some operations are up to 20x slower in 2.80!

Here is an example:

import time

import bpy


def add_cylinder():
    window = bpy.context.window_manager.windows[0]
    ctx = {'window': window, 'screen': window.screen}
    start = time.clock()
    bpy.ops.mesh.primitive_cylinder_add(
        ctx,
        radius=1, depth=1,
        location=(1.5, 0.0, 1),
        enter_editmode=False,
    )
    print(time.clock() - start)
    print()
    return 1


bpy.app.timers.register(add_cylinder)

Interestingly, these operations are only slow when I have loaded my 3D model (stats: Verts ~120.000, Faces ~200.000, Tris: ~250.000, Objects: ~1000).

When I run this example in an empty Blender file with 2.80 it is always fast (max 4ms, usually faster). In Blender 2.79 (code attached) with my model loaded it performs still very good (4ms to 8ms). However, running this on 2.80 with my model loaded it takes 60 to 85 ms!

I guess this is related to the new renderer. For a workaround it would be perfectly fine if I could disable rendering during my timer callbacks (if that helps), but I couldn't find anything in the API docs.

example-27.py

**System Information** Operating system: Windows 7, 64bit Graphics card: **Blender Version** Broken: (example: 2.80, edc1b0167518) Worked: (optional) 2.79b **Short description of error** I'm using Blender as a 3D live view controlled from an external application (see https://developer.blender.org/T62051 for more details). I just noticed that, compared to Blender 2.79, some operations are up to 20x slower in 2.80! Here is an example: ``` import time import bpy def add_cylinder(): window = bpy.context.window_manager.windows[0] ctx = {'window': window, 'screen': window.screen} start = time.clock() bpy.ops.mesh.primitive_cylinder_add( ctx, radius=1, depth=1, location=(1.5, 0.0, 1), enter_editmode=False, ) print(time.clock() - start) print() return 1 bpy.app.timers.register(add_cylinder) ``` Interestingly, these operations are only slow when I have loaded my 3D model (stats: Verts ~120.000, Faces ~200.000, Tris: ~250.000, Objects: ~1000). When I run this example in an empty Blender file with 2.80 it is always fast (max 4ms, usually faster). In Blender 2.79 (code attached) with my model loaded it performs still very good (4ms to 8ms). However, running this on 2.80 with my model loaded it takes 60 to 85 ms! I guess this is related to the new renderer. For a workaround it would be perfectly fine if I could disable rendering during my timer callbacks (if that helps), but I couldn't find anything in the API docs. [example-27.py](https://archive.blender.org/developer/F6951682/example-27.py)
Author

Added subscriber: @schlamar

Added subscriber: @schlamar
Member

Added subscriber: @JacquesLucke

Added subscriber: @JacquesLucke
Member

This is in an almost empty scene.

image.png

In your case, almost all time is spent in BKE_scene_graph_update_tagged. This is triggered by the newly created object.
Not sure if there is any way to avoid this currently.

This is in an almost empty scene. ![image.png](https://archive.blender.org/developer/F6951891/image.png) In your case, almost all time is spent in `BKE_scene_graph_update_tagged`. This is triggered by the newly created object. Not sure if there is any way to avoid this currently.
Author

Please note this is unrelated to application timers. The issue also shows if the add_cylinder is called directly without a custom context:

def add_cylinder():
    start = time.clock()
    bpy.ops.mesh.primitive_cylinder_add(
        radius=1, depth=1,
        location=(1.5, 0.0, 1),
    )
    print(time.clock() - start)
    print()


add_cylinder()

So I assume all Python plugins are affected by this performance regression!

You can reproduce this for example with the classroom demo (https://download.blender.org/demo/test/classroom.zip). With this example primitive_cylinder_add is ~10x slower on 2.80.

I'm not sure if I read your performance graph correctly, but isn't the scene update called before the execution of the operator?

Please note this is unrelated to application timers. The issue also shows if the `add_cylinder` is called directly without a custom context: ``` def add_cylinder(): start = time.clock() bpy.ops.mesh.primitive_cylinder_add( radius=1, depth=1, location=(1.5, 0.0, 1), ) print(time.clock() - start) print() add_cylinder() ``` So I assume **all** Python plugins are affected by this performance regression! You can reproduce this for example with the classroom demo (https://download.blender.org/demo/test/classroom.zip). With this example `primitive_cylinder_add` is ~10x slower on 2.80. I'm not sure if I read your performance graph correctly, but isn't the scene update called *before* the execution of the operator?
Author

Running the script multiple times in an empty scene, performance gets worse for every new object. Starting with ~2ms for the first object, duration with 100 added cylinders is ~12ms.

Running the script multiple times in an empty scene, performance gets worse for every new object. Starting with ~2ms for the first object, duration with 100 added cylinders is ~12ms.
Campbell Barton was assigned by Sebastian Parborg 2019-04-17 14:28:47 +02:00
Author

Any updates here? I think this is a high priority issue for the 2.80 release.

It looks like this is an exponential performance degradation, so with very high detailed models with a lot of objects Python plugins could be even unusable.

Any updates here? I think this is a high priority issue for the 2.80 release. It looks like this is an exponential performance degradation, so with very high detailed models with a lot of objects Python plugins could be even unusable.
Author

This issue still exists in the final 2.80 release. primitive_cylinder_add takes ~40 ms in 2.80 running in the classroom demo. On 2.79, it only takes around 4 ms.

This issue still exists in the final 2.80 release. `primitive_cylinder_add` takes ~40 ms in 2.80 running in the classroom demo. On 2.79, it only takes around 4 ms.
Campbell Barton was unassigned by Dalai Felinto 2019-12-23 16:34:36 +01:00

Added subscriber: @ideasman42

Added subscriber: @ideasman42

Added subscriber: @mano-wii

Added subscriber: @mano-wii

Changed status from 'Confirmed' to: 'Archived'

Changed status from 'Confirmed' to: 'Archived'
Germano Cavalcante self-assigned this 2020-01-29 19:26:14 +01:00

Operators add a lot of overhead to a script. For example, they add memory-consuming undo stacks.
Therefore, they should be avoided in loops or functions called very often.
You should look for other ways to create a primitive cylinder other than thougth an operator.
Also there are optimization plans in this area (IDs) -> https://code.blender.org/2020/01/2020-blender-big-projects/

Although it is slower than 2.79, this is not considered a bug as such a solution should be avoided in scripts.

Operators add a lot of overhead to a script. For example, they add memory-consuming undo stacks. Therefore, they should be avoided in loops or functions called very often. You should look for other ways to create a primitive cylinder other than thougth an operator. Also there are optimization plans in this area (IDs) -> https://code.blender.org/2020/01/2020-blender-big-projects/ Although it is slower than 2.79, this is not considered a bug as such a solution should be avoided in scripts.
Sign in to join this conversation.
No Milestone
No project
4 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender-addons#63662
No description provided.