google / google/protobuf.dart

`PbList` checks element type twice on each add

Open
#977 9 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dart
Stars
572
Forks
196
Avg merge
1h 59m
Merged PRs (30d)
2

Description

I don't think this can be fixed, but it's still worth documenting this issue somewhere probably..

`PbList.add` currently checks the element type, and then calls `GrowableList.add`, which checks the element type again.

Here's the relevant Wasm when compiled with `-O2`: (the default optimization setting when building Flutter apps)

```wat
(func $PbList.add (;353;) (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
(local $var2 (ref $PbList))
(local $var3 (ref $_Type))
(local $var4 (ref $#Closure-0-1))
local.get $var0
ref.cast $PbList
local.tee $var2
struct.get $PbList $field2
local.set $var3
local.get $var1
ref.is_null
if (result i32)
local.get $var3
struct.get $_Type $field2
else
local.get $var3
local.get $var1
ref.as_non_null
call $_InterfaceType._checkInstance
end
...
)

(func $GrowableList.add (;174;) (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
(local $var2 (ref $_Type))
(local $var3 (ref $WasmListBase))
(local $var4 (ref $#Top))
(local $var5 i64)
(local $var6 i32)
local.get $var0
ref.cast $WasmListBase
local.tee $var3
struct.get $WasmListBase $field2
local.set $var2
local.get $var1
ref.is_null
if (result i32)
local.get $var2
struct.get $_Type $field2
else
block $label0 (result i32)
i32.const 1
local.get $var2
struct.get $_Type $field0
local.tee $var6
i32.const 8
i32.eq
br_if $label0
drop
local.get $var1
ref.as_non_null
local.set $var4
local.get $var6
i32.const 12
i32.eq
if
local.get $var2
local.get $var4
call $_InterfaceType._checkInstance
br $label0
end
local.get $var2
local.get $var4
local.get $var2
struct.get $_Type $field0
i32.const 287
i32.add
call_indirect (param (ref $_Type) (ref $#Top)) (result i32)
end $label0
end
i32.eqz
if
local.get $var1
local.get $var2
call $_TypeError._throwAsCheckError
end
...
)
```

Now consider a packed field decoding loop, with #959.

For each 4-byte integer, we box the integer, and then check the type of it twice.

To have an idea of how much we could save, if I omit type checks in the generated code with `-O4`, here's before and after:

```
// Before
protobuf_PackedInt32Decoding(RunTimeRaw): 49775.0 us.

// After
protobuf_PackedInt32Decoding(RunTimeRaw): 36812.5 us.
```

That's 26% of decoding time wasted on redundant type checks.

Contributor guide

Open the contributing guide

Research direction

Start by reviewing the PbList.add and GrowableList.add entry points described in the issue, then compare the packed field decoding loop referenced with #959. Document that element types are checked twice and include the reported protobuf_PackedInt32Decoding benchmark; the issue does not name a documentation file or test to update.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
performance
Issue type
Documentation
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.