Skip to content

PbList checks element type twice on each add #977

@osa1

Description

@osa1

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)

(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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions