Skip to content

starlark: optimized dispatch of built-in methods - #645

Draft
adonovan wants to merge 1 commit into
masterfrom
exp-builtin
Draft

adonovan wants to merge 1 commit into
masterfrom
exp-builtin

Conversation

@adonovan

Copy link
Copy Markdown
Collaborator

This PR is a sketch of an alternative approach to supporting faster dispatch of calls to built-in methods by avoiding the allocation ordinarily performed by Builtin.BindReceiver when a method is accessed and then immediately called as in x.f().

The bound Builtin is still created for g=x.f; g(), or for values of x that do not implement the new HasBuiltinMethods interface.

Details:

  • Adds HasBuiltinMethods interface implemented by all the interpreter's built-in types.

  • Builtin now has two constructors: the legacy NewBuiltin, and a new one (NewBuiltinMethod) that takes a function of this form

    func(thread *Thread, name string, recv Value, args Tuple, kwargs []Tuple) (Value, error)
    -----------------------
    avoiding the need to allocate a Builtin with BindReceiver. The legacy one wraps the newone, possibly at an unacceptable allocation cost since it calls BindReceiver on every legacy call. The recv argument is nil for non-method calls.

  • x.f(...) is compiled to recv, callable = ATTR_METHOD(x, "f") res = CALL(recv, callable, ...) with a special CALL flag indicating that a receiver is provided because we used ATTR_METHOD. If callable is an unbound Builtin, the recv operand is used; otherwise the Builtin supplies the receiver.

WORK IN PROGRESS. Some parts are slop.

This PR is a sketch of an alternative approach to supporting
faster dispatch of calls to built-in methods by avoiding the
allocation ordinarily performed by Builtin.BindReceiver when
a method is accessed and then immediately called as in x.f().

The bound Builtin is still created for g=x.f; g(), or for
values of x that do not implement the new HasBuiltinMethods
interface.

Details:
- Adds HasBuiltinMethods interface implemented by all the
  interpreter's built-in types.

- Builtin now has two constructors: the legacy NewBuiltin, and a new
  one (NewBuiltinMethod) that takes a function of this form

    func(thread *Thread, name string, recv Value, args Tuple, kwargs []Tuple) (Value, error)
                         -----------------------
  avoiding the need to allocate a Builtin with BindReceiver.
  The legacy one wraps the newone, possibly at an unacceptable allocation cost
  since it calls BindReceiver on every legacy call.
  The recv argument is nil for non-method calls.

- x.f(...) is compiled to
      recv, callable = ATTR_METHOD(x, "f")
      res = CALL(recv, callable, ...)
  with a special CALL flag indicating that a receiver is provided
  because we used ATTR_METHOD. If callable is an unbound Builtin,
  the recv operand is used; otherwise the Builtin supplies the
  receiver.

WORK IN PROGRESS.  Some parts are slop.
@adonovan

Copy link
Copy Markdown
Collaborator Author

Here's the benchstat delta. The big gains are on very unnatural microbenchmarks, so it seems like a lot of trouble for little gain.

   Benchmark                                  |  HEAD~1  ( sec/op ) |  HEAD  ( sec/op ) |    Time Delta     |  HEAD~1  ( allocs/op ) |  HEAD  ( allocs/op ) |   Allocs Delta
  --------------------------------------------|---------------------|-------------------|-------------------|------------------------|----------------------|-------------------
    Starlark/bench_builtin_method-8           |      107.43 µs      |     93.65 µs      |      -12.83%      |          2001          |         1001         |      -49.98%
    Starlark/bench_string_startswith-8        |      125.40 ns      |     93.78 ns      |      -25.22%      |           3            |          1           |      -66.67%
    Starlark/bench_issubset_duplicate_same-8  |      25.99 µs       |     24.74 µs      |      -4.80%       |           6            |          5           |      -16.67%
    Starlark/bench_issubset_unique_same-8     |      29.49 µs       |     28.83 µs      |      -2.22%       |           8            |          7           |      -12.50%
    Starlark/bench_dict_equal-8               |      25.93 µs       |     25.57 µs      |      -1.37%       |           0            |          0           |         ~
    Starlark/bench_calling-8                  |      176.0 µs       |     178.7 µs      |   ~ ( p=0.151 )   |          303           |         303          |         ~
    Starlark/bench_mix-8                      |      60.28 µs       |     61.88 µs      |      +2.66%       |          483           |         483          |         ~
    Starlark/bench_bigint-8                   |      105.4 µs       |     110.5 µs      |      +4.85%       |          5006          |         5006         |         ~
    Starlark/bench_gauss-8                    |      5.341 ms       |     5.534 ms      |   ~ ( p=0.222 )   |         132320         |        132320        |         ~
    Program/compile-8                         |      152.8 µs       |     152.0 µs      |   ~ ( p=0.841 )   |          1596          |         1596         |         ~
    Program/read-8                            |      43.15 µs       |     42.33 µs      |   ~ ( p=0.421 )   |           5            |          5           |         ~
   Geomean across all benchmarks              |                     |                   |      -0.40%       |                        |                      |      -5.45%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant