Page MenuHome

Anim: Keylist drawing optimization by using arrays.
ClosedPublic

Authored by Jeroen Bakker (jbakker) on Jul 28 2021, 12:28 PM.
Subscribers
None
Tokens
"Love" token, awarded by hitrpr."Love" token, awarded by chironamo."Love" token, awarded by PrettyFireNOI7."Like" token, awarded by erickblender.

Details

Summary

Change data structure of keylists. Reducing the balancing overhead and therefore increases performance.

FunctionMasterPatch
draw_summary_channel0.202105s0.083874s

When adding items to the keylist it will store it in a linked list. This linked list is
accompanied with the length (key_len) and a last_accessed_column. last_accessed_column is a cursor
that improve the performance when adding new items as they are mostly ordered by frame numbers.
last_accessed_column is reset when a new fcurve/mask/... is added to the keylist.

Before searching or array access. the listbase needs to be converted to an array.
ED_keylist_prepare_for_direct_access. After that the caller can use
ED_keylist_find_* or ED_keylist_array* functions.

The internal array can also be accessed via the ED_keylist_listbase function.
The items inside the array link to the previous/next item in the list.

Future optimizations:

Diff Detail

Repository
rB Blender
Branch
arcpatch-D12052 (branched from master)
Build Status
Buildable 16260
Build 16260: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
  • Incorrect checks to find columns.
  • Incorrect checks for equal.
  • Use BLI_assert_msg.
  • Use direct access for drawing keyframes.
  • Store len of keylist in local var.
  • Renamed to ED_keylist_prepare_for_direct_access.
Jeroen Bakker (jbakker) requested review of this revision.Aug 4 2021, 5:20 PM
Jeroen Bakker (jbakker) retitled this revision from WIP: keylist drawing optimization. to Anim: Keylist drawing optimization by using arrays..
Jeroen Bakker (jbakker) edited the summary of this revision. (Show Details)
Jeroen Bakker (jbakker) edited the summary of this revision. (Show Details)Aug 4 2021, 5:27 PM
Sybren A. Stüvel (sybren) requested changes to this revision.Aug 5 2021, 4:55 PM

A cursor that as items are normally added ordered by frame number.

ehm what?

The code and comments seem to use the words "keys" and "key columns" for the same thing, which is confusing.

The code could also get another const pass, especially in the float cfra parameters.

Other remarks inline.

source/blender/editors/animation/keyframes_draw.c
404–406

I don't quite understand the spacing of these blocks (this one and similar ones below). Does this mean that ED_keylist_prepare_for_direct_access() belongs more to the creation fo the keylist, rather than to the code that needs the direct access? I'd expect to opposite.

To me it would make sense to even have draw_keylist() call ED_keylist_prepare_for_direct_access(); after all, calling it more than once for the same keylist won't hurt.

source/blender/editors/animation/keyframes_keylist.c
55

const, also next function.

62

This needs a comment to explain what it's for.

63

Keys don't have a length. This "keys" probably means something else, then, but that's not clear when you read this struct from top to bottom.

64

Given that the runtime field is a struct and not a pointer, this struct always "has" a runtime. Maybe is_runtime_initialised or something along those lines is better?

69

So a field called "keys" contains "key columns"? Just call it key_columns then.

73

What does init mean?

76

What is "building"? And why are the keys built? I think that "When building" and "After building" need a comma, to separate subclause from main clause ("After building, the keys are ....")

78

What does this ListBase contain? How can it contain individual keys?

116

Why is there an index called index and another one called i? Can't they both be index?

195

Add a comment that explains this performs a binary search. The code is a lot easier to follow when you know what it's supposed to be doing.

195

const

211

const

224

(A - C < B - C) is the same as (A < B), so this expression can be simplified. Also one set of parentheses can be removed.

Also, a comment that explains why this comparison is necessary would be good. If you want to ensure best_result is the one closest to cfra, the comparison needs the subtraction, but then misses an fabsf() call.

238

There is a lot of commonality between the previous function and this one. Wouldn't it be possible to abstract out the binary search?

At the minimum, add a comment that explains why this is not simply ED_keylist_find_next(keylist, cfra)->prev.

330–331

This also needs BLI_assert_msg(...) because otherwise the value stored here isn't trustworthy.

349–361

I think this would be easier to read when the has_runtime cases are split up:

ActKeyColumn *first_column;
ActKeyColumn *last_column;
if (keylist->has_runtime) {
  first_column = &keylist->runtime.keys[0];
  last_column = &keylist->runtime.keys[keylist->keys_len - 1];
}
else {
  first_column = keylist->init.keys.first;
  last_column = keylist->init.keys.last;
}
r_frame_range->min = first_column->cfra;
r_frame_range->max = last_column->cfra;
580

Why is this doing a linear search instead of the binary search of the other functions?

582–590

Why don't these comparisons use is_cfra_lt()?

603–604

Using the "last accessed column" here looks like tight coupling with other functions. What is expected of that column? Why is it a good idea to walk from there? This needs to be documented in the code.

625

This needs a message. Why is it bad to call this function when it has a runtime?

644

What is this commented-out code? I've seen it in other places as well. I think that given the fact that is_cfra_lt() doesn't define a strict ordering (there are values where both is_cfra_lt(A, B) and is_cfra_lt(B, A) are true) it's better to just keep this a plain else.

source/blender/editors/include/ED_keyframes_keylist.h
155

Any reason this is a uint64_t and not a size_t?

This revision now requires changes to proceed.Aug 5 2021, 4:55 PM
Jeroen Bakker (jbakker) edited the summary of this revision. (Show Details)Aug 6 2021, 8:22 AM
Jeroen Bakker (jbakker) marked 18 inline comments as done.
  • Cleanup: use const float cfra.
  • Deduplicated call to ED_keylist_prepare_for_direct_access during drawing.
  • Renamed keys_len to column_len.
  • Renames keys -> key_columns (and related renaming).
  • Split ED_keylist_runtime_init into multiple functions for clarity.
  • Improved readability of ED_keylist_frame_range.
  • Added comments why the internal find methods cannot use runtime.
  • Use size_t for ED_keylist_array_len.
source/blender/editors/animation/keyframes_draw.c
404–406

The second option would make the keylist not const inside the draw_keylist function.

I Would rather add a function prepare_keylist_for_drawing and use separate calls. This way it is clear. (let a function only do one thing and one thing only...)

Secondly to reduce code duplication could add a new function that calls both.

source/blender/editors/animation/keyframes_keylist.c
330–331

I considered it previously, but didn't go down that route as:

  • columns_len value is always accurate.
  • would reduce readability as there internal checks cannot use this function (ED_keylist_add_or_update_column) for example.
580

This function is used when the runtime isn't initialized yet and bin searching isn't possible. Will add a comment.
Using bin searching in this place reduces the performance. Currently in master the bin searching happens here and this patch optimizes this to use the last_accessed cursor.

582–590

Negating is_cfra_lt would be incorrect as it uses a threshhold.

source/blender/editors/include/ED_keyframes_keylist.h
155

Same reason as for BLI_array.hh: size_t is architecture dependent, uint64_t isn't.
I don't have a strong opinion for it in this case. 4M columns is already plenty :-)

Jeroen Bakker (jbakker) planned changes to this revision.Aug 6 2021, 12:37 PM

Use template functions to reduce the code duplication in the ED_keylist_find_* functions

source/blender/editors/animation/keyframes_draw.c
404–406

Good point about the const-ness, keeping those separated is a good idea.

source/blender/editors/animation/keyframes_keylist.c
330–331

Ok.

580

👌

source/blender/editors/include/ED_keyframes_keylist.h
155

In that case, I'd suggest following the style guide:

If your code is a container with a size, be sure its size-type is large enough for any possible usage. When in doubt, use a larger type like int64_t.
Don’t use unsigned integers to indicate that a value is non-negative, use assertions instead.

So uint64_tint64_t. The biggest advantage of a signed type is that certain miscalculations produce an invalid number (negative instead of positive bazillions).

Jeroen Bakker (jbakker) marked 4 inline comments as done.
  • Cleanup: removed comments after else.
  • minimized condition checks in if statements.
Jeroen Bakker (jbakker) planned changes to this revision.Aug 6 2021, 3:03 PM
  • Merge branch 'master' into arcpatch-D12052
  • Add constructor/destructor to AnimKeylist.
  • Use optional for last_accessed_column.
  • Use blender::Array for runtime->key_columns.
Jeroen Bakker (jbakker) planned changes to this revision.Aug 9 2021, 4:53 PM
  • Merge branch 'master' into arcpatch-D12052
  • Use clib binsearch.
Jeroen Bakker (jbakker) marked an inline comment as done.
  • Changed return type of ED_keylist_array_len from size_t to int64_t.
  • Merge branch 'master' into arcpatch-D12052
Jeroen Bakker (jbakker) planned changes to this revision.Aug 16 2021, 4:18 PM

Issues:

  • Find next/prev returns first element always.
  • Possible crash when building blocks.
  • Make find_next/prev consistent with master.
  • Solve edge cases when finding any in between.

It's a really nice performance improvement! LGTM.

This revision is now accepted and ready to land.Sep 10 2021, 12:17 PM