Skip to content

Optimize JSONMessageCodec UTF-8 conversion - #190529

Merged
auto-submit[bot] merged 3 commits into
flutter:masterfrom
FelixMittermeier:optimize-json-message-codec
Aug 20, 2026
Merged

auto-submit[bot] merged 3 commits into
flutter:masterfrom
FelixMittermeier:optimize-json-message-codec

Conversation

@FelixMittermeier

@FelixMittermeier FelixMittermeier commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

JSONMessageCodec currently serializes values to a JSON String and then passes that string through StringCodec for UTF-8 conversion. Decoding performs the reverse sequence.

This change uses cached JSON/UTF-8 converters directly, reducing conversion work and temporary allocations while preserving the existing wire format. JSONMethodCodec also benefits because it delegates its encoding and decoding to JSONMessageCodec.

This affects framework channels such as text input (TextField/EditableText), clipboard and haptics, system UI, navigation, localization, and keyboard/system events. It does not affect StandardMessageCodec or standalone jsonEncode/jsonDecode calls.

Median of nine samples comparing the old and new implementations in the same process:

Environment and payload Encode Decode
macOS arm64 AOT, 186 bytes 1.03 → 0.75 µs (-27%) 0.58 → 0.42 µs (-29%)
macOS arm64 AOT, 4.15 KB 39.4 → 25.6 µs (-35%) 29.4 → 15.3 µs (-48%)
Physical iPad, AOT profile, 186 bytes 1.35 → 0.98 µs (-28%) 0.79 → 0.57 µs (-28%)
Physical iPad, AOT profile, 4.15 KB 48.2 → 30.7 µs (-36%) 36.4 → 20.1 µs (-45%)

Also added a regression test that verifies the exact UTF-8 wire format, a four-byte Unicode character crossing the encoder’s default 256-byte chunk boundary, and decoding from a bounded ByteData view. The codec tests passed on both the Dart VM and Chrome, along with the related text-input/platform-channel tests and analysis.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@github-actions github-actions Bot added the framework flutter/packages/flutter repository. See also f: labels. label Aug 4, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors JSONMessageCodec to use JsonUtf8Encoder and a fused UTF-8/JSON decoder directly instead of routing through StringCodec, and adds a test to verify UTF-8 encoding and chunk boundaries. Feedback suggests optimizing encodeMessage by checking if the result of _encoder.convert is already a Uint8List to avoid redundant memory allocations and copies.

Comment thread packages/flutter/lib/src/services/message_codecs.dart
@Piinks
Piinks requested a review from elliette August 4, 2026 22:16
Comment thread packages/flutter/lib/src/services/message_codecs.dart
@elliette

elliette commented Aug 5, 2026

Copy link
Copy Markdown
Member

Hi @FelixMittermeier! Thanks for the contribution! Do you mind listing an issue in the PR description (see "I listed at least one issue that this PR fixes in the description above" in checklist above). If one does not already exist, please file one. Thanks!

@elliette elliette added the CICD Run CI/CD label Aug 5, 2026
@FelixMittermeier

Copy link
Copy Markdown
Contributor Author

@elliette Ah sorry, I clicked too fast yesterday. I unchecked this point in the PR description now. There is no existing issue for exactly this topic and IMO it also does not really make sense here to create a new one because there is no actual problem/bug. The current PR just optimises performance but does not fix any broken behavior etc.

Just found this quick win while benchmarking my app and searching for possible improvements.

@elliette elliette left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good besides a comment about the test below. Thanks for the performance improvement and for all of the profiling you did.

Comment on lines +163 to +178
test('should preserve UTF-8 encoding and ByteData bounds across encoder chunks', () {
// Place a four-byte character across JsonUtf8Encoder's default 256-byte chunk boundary.
final message = '${'a' * 254}\u{1F602}';
final Uint8List expected = utf8.encode(jsonEncode(message));

final ByteData encoded = json.encodeMessage(message)!;
expect(Uint8List.sublistView(encoded), orderedEquals(expected));

final padded = Uint8List(expected.length + 2)
..[0] = 0x5b
..setRange(1, expected.length + 1, expected)
..[expected.length + 1] = 0x5d;
final view = ByteData.sublistView(padded, 1, expected.length + 1);

expect(json.decodeMessage(view), message);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also passes on master. Is there anything else you can test that will fail if your change gets reverted?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to make a unit test fail when this change is reverted, since the optimization preserves the same result before and after. Do you think of anything specifically?

Comment on lines +97 to +98
static final JsonUtf8Encoder _encoder = JsonUtf8Encoder();
static final Converter<List<int>, Object?> _decoder = utf8.decoder.fuse(json.decoder);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it is best practice to keep these around. The memory impact should be negligible.

@loic-sharma loic-sharma added the autosubmit Merge PR when tree becomes green via auto submit App label Aug 14, 2026
@auto-submit

auto-submit Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/190529, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR.

@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Aug 14, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 17, 2026
@loic-sharma loic-sharma added the CICD Run CI/CD label Aug 17, 2026
@justinmc

Copy link
Copy Markdown
Contributor

There's a failure that looks like an infra problem, but I can't rerun it for some reason. @FelixMittermeier could you push a merge commit to cause it to run again?

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 19, 2026
@FelixMittermeier

Copy link
Copy Markdown
Contributor Author

@justinmc Alright done 👍

@loic-sharma loic-sharma added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels Aug 19, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Aug 20, 2026
Merged via the queue into flutter:master with commit 4bdb8c6 Aug 20, 2026
28 of 29 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Aug 20, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Aug 22, 2026
Roll Flutter from c2437523d308 to 65c9a8dc60bc (195 revisions)

flutter/flutter@c243752...65c9a8d

2026-08-22 [email protected] Roll ICU from d578f2e8b7bd to 8cc91d9b6ab9 (1 revision) (flutter/flutter#191542)
2026-08-22 [email protected] Roll Skia from 666a9b3d5cf0 to ad2106c0bb64 (3 revisions) (flutter/flutter#191527)
2026-08-22 [email protected] Roll Fuchsia Test Scripts from KaOq3EE4qJ9fnaaaK... to 0iCv10IlKfiilEBOU... (flutter/flutter#191524)
2026-08-22 [email protected] [flutter_tools] Add tests for negative lookahead regex in test runner and batch entrypoints (flutter/flutter#191438)
2026-08-22 [email protected] Roll Skia from 0c37868737fa to 666a9b3d5cf0 (2 revisions) (flutter/flutter#191518)
2026-08-21 [email protected] Revert "[input] Migrate fuchsia.ui.pointerinjector to TouchSource (#190855) (flutter/flutter#191509)
2026-08-21 [email protected] Fixes windows gallery benchmarks by forcing mobile layout (flutter/flutter#191507)
2026-08-21 [email protected] [flutter_tools] Restrict WebAssetServer source resolution to source map extensions (flutter/flutter#191501)
2026-08-21 [email protected] Roll Skia from f6900c5b8439 to 0c37868737fa (2 revisions) (flutter/flutter#191504)
2026-08-21 [email protected] Refactor `FlutterDevice.connect` and VM service discovery (flutter/flutter#191221)
2026-08-21 [email protected] [flutter_tools] Fix crash when migrating flow-style exclude lists in analysis_options.yaml (flutter/flutter#191269)
2026-08-21 [email protected] [rules] Add packages/flutter_tools/gradle/AGENTS.md rules (flutter/flutter#191486)
2026-08-21 [email protected] [flutter_tools] refactor CLI argument architecture with typed option descriptors and bundles (PoC) (flutter/flutter#191018)
2026-08-21 [email protected] tools: Extract Dart SDK to temp directory before moving to final location (flutter/flutter#191263)
2026-08-21 [email protected] [web] Move CanvasKit fragment shader classes to canvaskit/fragment_shader.dart (flutter/flutter#191451)
2026-08-21 [email protected] Roll Fuchsia Linux SDK from GCQlmt6h-esJsNubS... to ic6GjOSn-KN508XyK... (flutter/flutter#191485)
2026-08-21 [email protected] [Widget Preview] Isolate PageStorage scope in widget preview group expansion tile (flutter/flutter#191378)
2026-08-21 [email protected] [flutter_tools] Deprecate --build and --no-build flags on flutter run (flutter/flutter#191358)
2026-08-21 [email protected] Roll Skia from 70988bed1b3b to f6900c5b8439 (2 revisions) (flutter/flutter#191481)
2026-08-21 [email protected] Roll Packages from 1785501 to 252bb33 (6 revisions) (flutter/flutter#191480)
2026-08-21 [email protected] Roll Fuchsia Linux SDK from 20IJas24bZiNmCZTK... to GCQlmt6h-esJsNubS... (flutter/flutter#191415)
2026-08-21 [email protected] Roll Skia from 2ba6971bd0d1 to 70988bed1b3b (2 revisions) (flutter/flutter#191476)
2026-08-21 [email protected] Roll Skia from 1d5f72537ba6 to 2ba6971bd0d1 (2 revisions) (flutter/flutter#191473)
2026-08-21 [email protected] Roll Skia from 2c25efd2e369 to 1d5f72537ba6 (1 revision) (flutter/flutter#191472)
2026-08-21 [email protected] Roll Skia from 09b1b810850a to 2c25efd2e369 (9 revisions) (flutter/flutter#191470)
2026-08-21 [email protected] [Impeller] fix position of cached single glyph text shadows (flutter/flutter#191325)
2026-08-21 [email protected] Roll Skia from abdf8821f313 to 09b1b810850a (15 revisions) (flutter/flutter#191458)
2026-08-21 [email protected] Document that programmatic TextEditingController changes do not run input formatters (flutter/flutter#190166)
2026-08-21 [email protected] Adds new gallery benchmarks to windows (skia and impeller) (flutter/flutter#191454)
2026-08-21 [email protected] iOS: Deprecate FlutterEngine.isGpuDisabled (flutter/flutter#191393)
2026-08-21 [email protected] Revert: [web] Unskip decoration image lerp tests (flutter/flutter#191462)
2026-08-20 [email protected] android_hardware_smoke_test: Improve reliability (flutter/flutter#191374)
2026-08-20 [email protected] Don't size or offset the Android platform view before it is laid out (flutter/flutter#190895)
2026-08-20 [email protected] [iOS][add2app] Skip building SwiftPM plugins when generating CocoaPods artifacts (flutter/flutter#190736)
2026-08-20 [email protected] Optimize JSONMessageCodec UTF-8 conversion (flutter/flutter#190529)
2026-08-20 [email protected] [FML] Replace deprecated wstring_convert with Win32 APIs (flutter/flutter#191394)
2026-08-20 [email protected] SliverFillRemaining extends beyond viewport size when fillOverscroll is true (flutter/flutter#191236)
2026-08-20 [email protected] [flutter_tools] Update argParser usageLineLength when --wrap-column is passed (flutter/flutter#191264)
2026-08-20 [email protected] [flutter_tools] Fix UNC path resolution in depfile parsing on Windows (flutter/flutter#191265)
2026-08-20 [email protected] [web] Unskip decoration image lerp tests (flutter/flutter#191426)
2026-08-20 [email protected] Do not inject 'type' and 'method' into service extension responses (flutter/flutter#190946)
2026-08-20 [email protected] [flutter_tools] Fix crash in symbolize command on stream error (flutter/flutter#191273)
2026-08-20 [email protected] [flutter_tools] Prevent deletion of shared native asset hooks outputs as stale (flutter/flutter#191272)
2026-08-20 [email protected] [flutter_tools] Support package wildcard assets in app pubspec (flutter/flutter#191266)
2026-08-20 [email protected] Add SliverClipRect and SliverClipRRect (flutter/flutter#179003)
2026-08-20 [email protected] [flutter_tools] Implement Diagnostics extension slice and doctor integration (flutter/flutter#191162)
...
@justinmc

Copy link
Copy Markdown
Contributor

Hmm I wouldn't have expected an app size change from this.

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

Labels

CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants