Skip to content

Performance: Flutter codec loading, cache lifecycle, and Surface teardown bottlenecks #61

Description

@q760440238

Problem

PowerImage's default HTTP path and native Surface lifecycle had several independent sources of latency and cross-request blocking:

  • encoded-byte cache misses were not single-flight;
  • network/decoding work was not visibility-prioritized at the real first Codec.getNextFrame();
  • cache writes, forced flushes, and fine-grained LRU touches competed with first paint;
  • ordinary PNG/GIF/WebP could enter native/custom rendering paths instead of Flutter's codec/widget fast path;
  • native rendering fenced every frame instead of only teardown;
  • Surface release coordination could block unrelated engine/Surface generations;
  • native resources depended on global binding/cache hooks instead of the completer's last listener.

Reproducible physical-device comparison

Huawei VOG-AL10, Android 10 / API 29, Flutter 3.38.7, full AOT compilation, identical 512x512 loopback HTTP fixture, 160x160 target, 10 cold iterations. ExtendedImage is pinned to 10.1.0 and CachedNetworkImage to 3.4.1. P95 is nearest-rank.

Cold first displayed frame P50 P95 peak RSS median
PowerImage 41.967 ms 46.382 ms 251,406 KB
ExtendedImage 10.1.0 42.390 ms 63.427 ms 258,870 KB
CachedNetworkImage 3.4.1 75.667 ms 78.328 ms 259,114 KB

PowerImage is 1.0% faster than ExtendedImage at P50 and 26.9% faster at P95; versus CachedNetworkImage it is 44.5% / 40.8% faster.

Isolated A/B results for the proposed changes

Change Before P50/P95 After P50/P95 Delta
disk ImmutableBuffer.fromFilePath 32.834 / 38.803 ms 27.509 / 36.203 ms -16.2% / -6.7%
deferred/bounded cache maintenance 241.172 / 249.526 ms 229.213 / 232.482 ms -5.0% / -6.8%
schedule the real first getNextFrame() 45.145 / 77.059 ms 41.967 / 46.382 ms -7.0% / -39.8%
standard Flutter Image widget fast path 44.361 / 60.090 ms 42.646 / 59.193 ms -3.9% / -1.5%
animated raster queueBuffer average 0.560 / 0.582 ms 0.543 / 0.559 ms -3.0% / -3.8%

The final animated run submitted a median 826 frames versus 819 before the widget fast path, so the timing change is not caused by dropped frames. Private RSS stayed flat (126,916 KB before, 126,804 KB after).

Four final cache/lifecycle scenarios

Scenario iterations P50 P95
cold first frame 10 41.967 ms 46.382 ms
encoded-byte disk hit 20 27.936 ms 48.598 ms
Flutter ImageCache hit 10 15.878 ms 22.868 ms
release/rebuild 100 native Surfaces 5 962.568 ms 2,337.305 ms

All iterations reached their required real-frame completion marker. The final disk-hit distribution contains three 45-49 ms scheduling outliers; the isolated fromFilePath A/B above is the evidence for that particular change.

Raw AndroidX Macrobenchmark JSON, fixture methodology, exact versions, and memory figures are checked in here:
https://github.com/q760440238/power_image/tree/feature/android-animated-webp/example/android/macrobenchmark/results/2026-08-03-VOG-AL10

A corresponding implementation and reproducible benchmark suite is available in PR #59.

Activity

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

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