fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.
https://fzyzcjy.github.io/flutter_rust_bridge/
MIT License
4.29k stars 301 forks source link

[wasm][web] sending `UInt8List` to Rust Takes Approximately 1 second per MB and Hangs UI - because it is tested in debug mode #2301

Closed isosphere closed 2 months ago

isosphere commented 2 months ago

Describe the bug

When passing a UInt8List to a Rust function from Dart, the UI hangs for approximately 1 second per MB on my machine. I have created a minimal example, below.

The Rust function in question here doesn't actually do anything with the passed blob, it just returns a fixed string. The hang does not occur until the rust function is called, which implies to me that this is related to how types are translated and/or transferred.

#[flutter_rust_bridge::frb(sync)] // i've omitted this with no effect
pub fn do_nothing_with_blob(blob: Vec<u8>) -> String {
    format!("Hello, I didn't even look at the blob! Nyah!")
}
Future<String?> _processBlob() async {
  print("Making blob.");
  Uint8List blob = Uint8List(1024 * 1024 * 10); // 10MB
  print("Blob made, sending to Rust."); // this occurs immediately
  return doNothingWithBlob(blob: blob); // this hangs the UI for about 10 seconds
}

My end goal is to process a file in Rust that is "uploaded" from Dart via a file picker.

Steps to reproduce

Here's a repo that reproduces this behaviour: https://github.com/isosphere/frb_large_transfer_hang_reproduction

Logs

collapsable logs section ```shell frb_large_transfer_hang_reproduction git:(main) RUST_LOG=debug flutter_rust_bridge_codegen generate [2024-09-15T23:54:00.964Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/main.rs:24] cli=Cli { verbose: false, command: Generate(GenerateCommandArgs { watch: false, primary: GenerateCommandArgsPrimary { config_file: None, rust_input: None, dart_output: None, c_output: None, duplicated_c_output: None, rust_root: None, rust_output: None, dart_entrypoint_class_name: None, dart_format_line_length: None, dart_preamble: None, rust_preamble: None, no_dart_enums_style: false, no_add_mod_to_lib: false, llvm_path: None, llvm_compiler_opts: None, dart_root: None, no_build_runner: false, extra_headers: None, no_web: false, no_deps_check: false, default_external_library_loader_web_prefix: None, no_dart3: false, full_dep: false, local: false, enable_lifetime: false, type_64bit_int: false, no_default_dart_async: false, stop_on_error: false, dump: None, dump_all: false } }) } [2024-09-15T23:54:00.964Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/config/config_parser.rs:51] Found config file flutter_rust_bridge.yaml [2024-09-15T23:54:00.964Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/mod.rs:23] config=Config { base_dir: Some(""), rust_input: Some("crate::api"), dart_output: Some("lib/src/rust"), c_output: None, duplicated_c_output: None, rust_root: Some("rust/"), rust_output: None, dart_entrypoint_class_name: None, dart_format_line_length: None, dart_preamble: None, rust_preamble: None, dart_enums_style: None, add_mod_to_lib: None, llvm_path: None, llvm_compiler_opts: None, dart_root: None, build_runner: None, extra_headers: None, web: None, deps_check: None, dart3: None, full_dep: None, local: None, default_external_library_loader_web_prefix: None, dart_type_rename: None, enable_lifetime: None, type_64bit_int: None, default_dart_async: None, stop_on_error: None, dump: None, dump_all: None } meta_config=MetaConfig { watch: false } [2024-09-15T23:54:00.964Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/config/internal_config_parser/mod.rs:33] InternalConfig.parse base_dir="/home/matt/src/frb_large_transfer_hang_reproduction" [2024-09-15T23:54:01.123Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/mod.rs:26] internal_config=InternalConfig { controller: ControllerInternalConfig { watch: false, watching_paths: ["/home/matt/src/frb_large_transfer_hang_reproduction/rust/src"], exclude_paths: ["/home/matt/src/frb_large_transfer_hang_reproduction/rust/src/frb_generated.rs"], max_count: None }, preparer: PreparerInternalConfig { dart_root: "/home/matt/src/frb_large_transfer_hang_reproduction", deps_check: true, needs_ffigen: false }, parser: ParserInternalConfig { hir: ParserHirInternalConfig { rust_input_namespace_pack: RustInputNamespacePack { rust_input_namespace_prefixes: [Namespace { joined_path: "crate::api" }], rust_output_path_namespace: Namespace { joined_path: "crate::frb_generated" } }, rust_crate_dir: "/home/matt/src/frb_large_transfer_hang_reproduction/rust", third_party_crate_names: [] }, mir: ParserMirInternalConfig { rust_input_namespace_pack: RustInputNamespacePack { rust_input_namespace_prefixes: [Namespace { joined_path: "crate::api" }], rust_output_path_namespace: Namespace { joined_path: "crate::frb_generated" } }, force_codec_mode_pack: Some(CodecModePack { dart2rust: Pde, rust2dart: Pde }), default_stream_sink_codec: Sse, default_rust_opaque_codec: Moi, stop_on_error: false, enable_lifetime: false, type_64bit_int: false, default_dart_async: true } }, generator: GeneratorInternalConfig { api_dart: GeneratorApiDartInternalConfig { dart_enums_style: true, dart3: true, dart_decl_base_output_path: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust", dart_impl_output_path: TargetOrCommonMap { common: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust/frb_generated.dart", io: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust/frb_generated.io.dart", web: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust/frb_generated.web.dart" }, dart_entrypoint_class_name: "RustLib", dart_preamble: "", dart_type_rename: {} }, wire: GeneratorWireInternalConfig { dart: GeneratorWireDartInternalConfig { has_ffigen: false, web_enabled: true, llvm_path: ["/opt/homebrew/opt/llvm", "/usr/local/opt/llvm", "/usr/lib/llvm-9", "/usr/lib/llvm-10", "/usr/lib/llvm-11", "/usr/lib/llvm-12", "/usr/lib/llvm-13", "/usr/lib/llvm-14", "/usr/lib/", "/usr/lib64/", "C:/Program Files/llvm", "C:/msys64/mingw64"], llvm_compiler_opts: "", dart_root: "/home/matt/src/frb_large_transfer_hang_reproduction", extra_headers: "", dart_impl_output_path: TargetOrCommonMap { common: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust/frb_generated.dart", io: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust/frb_generated.io.dart", web: "/home/matt/src/frb_large_transfer_hang_reproduction/lib/src/rust/frb_generated.web.dart" }, dart_output_class_name_pack: DartOutputClassNamePack { entrypoint_class_name: "RustLib", api_class_name: "RustLibApi", api_impl_class_name: "RustLibApiImpl", api_impl_platform_class_name: "RustLibApiImplPlatform", wire_class_name: "RustLibWire", wasm_module_name: "RustLibWasmModule" }, default_external_library_loader: GeneratorWireDartDefaultExternalLibraryLoaderInternalConfig { stem: "rust_lib_frb_large_transfer_hang_reproduction", io_directory: "rust/target/release/", web_prefix: "pkg/" }, c_symbol_prefix: "frbgen_frb_large_transfer_hang_reproduction_" }, rust: GeneratorWireRustInternalConfig { rust_crate_dir: "/home/matt/src/frb_large_transfer_hang_reproduction/rust", web_enabled: true, rust_output_path: "/home/matt/src/frb_large_transfer_hang_reproduction/rust/src/frb_generated.rs", c_symbol_prefix: "frbgen_frb_large_transfer_hang_reproduction_", has_ffigen: false, default_stream_sink_codec: Sse, default_rust_opaque_codec: Moi, rust_preamble: "" }, c: GeneratorWireCInternalConfig { enable: false, rust_crate_dir: "/home/matt/src/frb_large_transfer_hang_reproduction/rust", rust_output_path: "/home/matt/src/frb_large_transfer_hang_reproduction/rust/src/frb_generated.rs", c_output_path: None, c_symbol_prefix: "frbgen_frb_large_transfer_hang_reproduction_" } } }, polisher: PolisherInternalConfig { duplicated_c_output_path: [], dart_format_line_length: 80, add_mod_to_lib: true, build_runner: true, web_enabled: true, dart_root: "/home/matt/src/frb_large_transfer_hang_reproduction", rust_crate_dir: "/home/matt/src/frb_large_transfer_hang_reproduction/rust", rust_output_path: "/home/matt/src/frb_large_transfer_hang_reproduction/rust/src/frb_generated.rs", c_output_path: None, enable_auto_upgrade: true }, dumper: DumperInternalConfig { dump_contents: [], dump_directory: "/home/matt/src/frb_large_transfer_hang_reproduction/rust/target/frb_dump" } } [2024-09-15T23:54:01.123Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/utils/dart_repository/dart_repo.rs:21] Guessing toolchain the runner is run into [2024-09-15T23:54:01.124Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:129] execute command: bin=sh args="-c \"flutter\" \"--version\"" current_dir=None cmd="sh" "-c" "\"flutter\" \"--version\"" [2024-09-15T23:54:01.398Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:140] command="sh" "-c" "\"flutter\" \"--version\"" stdout=Flutter 3.24.3 • channel stable • https://github.com/flutter/flutter.git Framework • revision 2663184aa7 (4 days ago) • 2024-09-11 16:27:48 -0500 Engine • revision 36335019a8 Tools • Dart 3.5.3 • DevTools 2.37.3 stderr= [2024-09-15T23:54:01.398Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/cargo_expand/mod.rs:26] run_cargo_expand manifest_dir= rust_crate_dir="/home/matt/src/frb_large_transfer_hang_reproduction/rust" [2024-09-15T23:54:01.399Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/cargo_expand/real.rs:60] Running cargo expand in '"/home/matt/src/frb_large_transfer_hang_reproduction/rust"' [2024-09-15T23:54:01.399Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:129] execute command: bin=cargo args="expand --lib --theme=none --ugly" current_dir=Some("/home/matt/src/frb_large_transfer_hang_reproduction/rust") cmd=cd "/home/matt/src/frb_large_transfer_hang_reproduction/rust" && RUSTFLAGS="--cfg frb_expand" "cargo" "expand" "--lib" "--theme=none" "--ugly" [2024-09-15T23:54:01.638Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:140] command=cd "/home/matt/src/frb_large_transfer_hang_reproduction/rust" && RUSTFLAGS="--cfg frb_expand" "cargo" "expand" "--lib" "--theme=none" "--ugly" stdout=#![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; pub mod api { pub mod simple { #[doc = "frb_encoded(235b6672622873796e63295d)"] pub fn do_nothing_with_blob(blob: Vec) -> String { ::alloc::__export::must_use({ let res = ::alloc::fmt::format(format_args!("Hello, I didn\'t even look at the blob! Nyah!")); res }) } #[doc = "frb_encoded(235b66726228696e6974295d)"] pub fn init_app() { flutter_rust_bridge::setup_default_user_utils(); } } } mod frb_generated { #![allow(non_camel_case_types, unused, non_snake_case, clippy::needless_return, clippy::redundant_closure_call, clippy::redundant_closure, clippy::useless_conversion, clippy::unit_arg, clippy::unused_unit, clippy::double_parens, clippy::let_and_return, clippy::too_many_arguments, clippy::match_single_binding, clippy::clone_on_copy, clippy::let_unit_value, clippy::deref_addrof, clippy::explicit_auto_deref, clippy::borrow_deref_ref, clippy::needless_borrow)] use flutter_rust_bridge::for_generated::byteorder::{ NativeEndian, ReadBytesExt, WriteBytesExt, }; use flutter_rust_bridge::for_generated::{ transform_result_dco, Lifetimeable, Lockable, }; use flutter_rust_bridge::{Handler, IntoIntoDart}; #[doc(hidden)] pub(crate) struct FrbWrapper(T); impl Clone for FrbWrapper { fn clone(&self) -> Self { FrbWrapper(self.0.clone()) } } impl PartialEq for FrbWrapper { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } } impl Eq for FrbWrapper {} impl std::hash::Hash for FrbWrapper { fn hash(&self, state: &mut H) { self.0.hash(state) } } impl From for FrbWrapper { fn from(t: T) -> Self { FrbWrapper(t) } } use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; pub struct MoiArc { object_id: Option, value: Option>, _phantom: PhantomData, } #[automatically_derived] impl ::core::fmt::Debug for MoiArc { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_struct_field3_finish(f, "MoiArc", "object_id", &self.object_id, "value", &self.value, "_phantom", &&self._phantom) } } impl Drop for MoiArc { fn drop(&mut self) { if let Some(object_id) = self.object_id { Self::decrement_strong_count(object_id); } } } impl AsRef for MoiArc { fn as_ref(&self) -> &T { self.value.as_ref().unwrap().as_ref() } } impl ::flutter_rust_bridge::for_generated::BaseArc for MoiArc { fn new(value: T) -> Self where T: Sized { let mut pool = T::get_pool().write().unwrap(); let object_id = pool.id_generator.next_id(); let value = Arc::new(value); let old_value = pool.map.insert(object_id, MoiArcPoolValue { ref_count: 1, value: value.clone() }); if !old_value.is_none() { ::core::panicking::panic("assertion failed: old_value.is_none()") }; Self { object_id: Some(object_id), value: Some(value), _phantom: PhantomData, } } fn try_unwrap(mut self) -> Result where T: Sized { let pool = &mut T::get_pool().write().unwrap(); if pool.map.get(&self.object_id.unwrap()).unwrap().ref_count == 1 { Self::decrement_strong_count_raw(self.object_id.unwrap(), pool); self.object_id.take().unwrap(); Ok(Arc::into_inner(self.value.take().unwrap()).unwrap()) } else { Err(self) } } fn into_inner(self) -> Option where T: Sized { self.try_unwrap().ok() } fn into_raw(mut self) -> usize { self.object_id.take().unwrap() } } impl Clone for MoiArc { fn clone(&self) -> Self { Self::increment_strong_count(self.object_id.unwrap()); Self { object_id: self.object_id, value: self.value.clone(), _phantom: PhantomData, } } } impl MoiArc { pub(crate) fn from_raw(raw: usize) -> Self where T: Sized { let map = &T::get_pool().read().unwrap().map; Self { object_id: Some(raw), value: Some(map.get(&raw).unwrap().value.clone()), _phantom: PhantomData, } } pub fn increment_strong_count(raw: usize) { let map = &mut T::get_pool().write().unwrap().map; map.get_mut(&raw).unwrap().ref_count += 1; } pub fn decrement_strong_count(raw: usize) { let mut pool = T::get_pool().write().unwrap(); let object = Self::decrement_strong_count_raw(raw, &mut pool); drop(pool); drop(object); } fn decrement_strong_count_raw(raw: usize, pool: &mut MoiArcPoolInner) -> Option> { let value = pool.map.get_mut(&raw).unwrap(); value.ref_count -= 1; if value.ref_count == 0 { pool.map.remove(&raw) } else { None } } } pub trait MoiArcValue: 'static { fn get_pool() -> &'static MoiArcPool; } type ObjectId = usize; pub type MoiArcPool = std::sync::RwLock>; pub struct MoiArcPoolInner { map: HashMap>, id_generator: IdGenerator, } impl Default for MoiArcPoolInner { fn default() -> Self { Self { map: HashMap::new(), id_generator: Default::default() } } } struct IdGenerator { next_id: ObjectId, } impl Default for IdGenerator { fn default() -> Self { Self { next_id: Self::MIN_ID } } } impl IdGenerator { const MIN_ID: ObjectId = 1; const MAX_ID: ObjectId = 2147483600; fn next_id(&mut self) -> ObjectId { let ans = self.next_id; self.next_id = if self.next_id >= Self::MAX_ID { Self::MIN_ID } else { self.next_id + 1 }; ans } } impl MoiArcPoolInner {} struct MoiArcPoolValue { ref_count: i32, value: Arc, } use ::flutter_rust_bridge::for_generated::decode_rust_opaque_nom; fn decode_rust_opaque_moi(ptr: usize) -> RustOpaqueMoi { RustOpaqueMoi::from_arc(MoiArc::::from_raw(ptr)) } use ::flutter_rust_bridge::for_generated::StdArc; use ::flutter_rust_bridge::RustOpaqueNom; /// Please refer to `RustOpaque` for doc. pub type RustOpaqueMoi = ::flutter_rust_bridge::for_generated::RustOpaqueBase>; /// A wrapper to support [arbitrary Rust types](https://cjycode.com/flutter_rust_bridge/guides/types/arbitrary). pub type RustOpaque = RustOpaqueMoi; use ::flutter_rust_bridge::RustAutoOpaqueNom; /// Please refer to `RustAutoOpaque` for doc. pub type RustAutoOpaqueMoi = ::flutter_rust_bridge::for_generated::RustAutoOpaqueBase>>; /// Usually this is unneeded, and just write down arbitrary types. /// However, when you need arbitrary types at places that are not supported yet, /// use `RustOpaqueOpaque`. pub type RustAutoOpaque = RustAutoOpaqueMoi; pub trait CstDecode { fn cst_decode(self) -> T; } impl CstDecode> for *mut S where *mut S: CstDecode { fn cst_decode(self) -> Option { (!self.is_null()).then(|| self.cst_decode()) } } pub trait SseDecode { fn sse_decode(deserializer: &mut ::flutter_rust_bridge::for_generated::SseDeserializer) -> Self; } pub trait SseEncode { fn sse_encode(self, serializer: &mut ::flutter_rust_bridge::for_generated::SseSerializer); } fn transform_result_sse(raw: Result) -> Result<::flutter_rust_bridge::for_generated::Rust2DartMessageSse, ::flutter_rust_bridge::for_generated::Rust2DartMessageSse> where T: SseEncode, E: SseEncode { use ::flutter_rust_bridge::for_generated::{Rust2DartAction, SseCodec}; match raw { Ok(raw) => Ok(SseCodec::encode(Rust2DartAction::Success, |serializer| { raw.sse_encode(serializer) })), Err(raw) => Err(SseCodec::encode(Rust2DartAction::Error, |serializer| { raw.sse_encode(serializer) })), } } pub struct StreamSink { base: ::flutter_rust_bridge::for_generated::StreamSinkBase, } #[automatically_derived] impl ::core::clone::Clone for StreamSink { #[inline] fn clone(&self) -> StreamSink { StreamSink { base: ::core::clone::Clone::clone(&self.base) } } } impl StreamSink { pub fn deserialize(raw: String) -> Self { Self { base: ::flutter_rust_bridge::for_generated::StreamSinkBase::deserialize(raw), } } } impl StreamSink { pub fn add(&self, value: T) -> Result<(), ::flutter_rust_bridge::Rust2DartSendError> where T: ::flutter_rust_bridge::IntoIntoDart, T2: ::flutter_rust_bridge::IntoDart { self.add_raw(::flutter_rust_bridge::for_generated::Rust2DartAction::Success, value) } pub fn add_error(&self, value: TR) -> Result<(), ::flutter_rust_bridge::Rust2DartSendError> where TR: ::flutter_rust_bridge::IntoIntoDart, T2: ::flutter_rust_bridge::IntoDart { self.add_raw(::flutter_rust_bridge::for_generated::Rust2DartAction::Error, value) } fn add_raw(&self, action: ::flutter_rust_bridge::for_generated::Rust2DartAction, value: TR) -> Result<(), ::flutter_rust_bridge::Rust2DartSendError> where TR: ::flutter_rust_bridge::IntoIntoDart, T2: ::flutter_rust_bridge::IntoDart { self.base.add_raw(::flutter_rust_bridge::for_generated::DcoCodec::encode(action, value.into_into_dart())) } } impl StreamSink where T: SseEncode { pub fn add(&self, value: T) -> Result<(), ::flutter_rust_bridge::Rust2DartSendError> { self.add_raw(::flutter_rust_bridge::for_generated::Rust2DartAction::Success, value) } pub fn add_error(&self, value: TR) -> Result<(), ::flutter_rust_bridge::Rust2DartSendError> { self.add_raw(::flutter_rust_bridge::for_generated::Rust2DartAction::Error, value) } pub fn add_raw(&self, action: ::flutter_rust_bridge::for_generated::Rust2DartAction, value: TR) -> Result<(), ::flutter_rust_bridge::Rust2DartSendError> { self.base.add_raw(::flutter_rust_bridge::for_generated::SseCodec::encode(action, |serializer| value.sse_encode(serializer))) } } impl ::flutter_rust_bridge::IntoIntoDart> for StreamSink { fn into_into_dart(self) -> StreamSink { ::core::panicking::panic("internal error: entered unreachable code") } } impl ::flutter_rust_bridge::IntoDart for StreamSink { fn into_dart(self) -> ::flutter_rust_bridge::for_generated::DartAbi { ::core::panicking::panic("internal error: entered unreachable code") } } pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.4.0"; pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -740289372; #[allow(missing_copy_implementations)] #[allow(non_camel_case_types)] #[allow(dead_code)] pub struct FLUTTER_RUST_BRIDGE_HANDLER { __private_field: (), } #[doc(hidden)] pub static FLUTTER_RUST_BRIDGE_HANDLER: FLUTTER_RUST_BRIDGE_HANDLER = FLUTTER_RUST_BRIDGE_HANDLER { __private_field: () }; impl ::lazy_static::__Deref for FLUTTER_RUST_BRIDGE_HANDLER { type Target = ::flutter_rust_bridge::DefaultHandler<::flutter_rust_bridge::for_generated::SimpleThreadPool>; fn deref(&self) -> &::flutter_rust_bridge::DefaultHandler<::flutter_rust_bridge::for_generated::SimpleThreadPool> { #[inline(always)] fn __static_ref_initialize() -> ::flutter_rust_bridge::DefaultHandler<::flutter_rust_bridge::for_generated::SimpleThreadPool> { { match (&FLUTTER_RUST_BRIDGE_CODEGEN_VERSION, &flutter_rust_bridge::for_generated::FLUTTER_RUST_BRIDGE_RUNTIME_VERSION) { (left_val, right_val) => { if !(*left_val == *right_val) { let kind = ::core::panicking::AssertKind::Eq; ::core::panicking::assert_failed(kind, &*left_val, &*right_val, ::core::option::Option::Some(format_args!("Please ensure flutter_rust_bridge\'s codegen ({0}) and runtime ({1}) versions are the same", FLUTTER_RUST_BRIDGE_CODEGEN_VERSION, flutter_rust_bridge::for_generated::FLUTTER_RUST_BRIDGE_RUNTIME_VERSION))); } } }; ::flutter_rust_bridge::DefaultHandler::new_simple(Default::default()) } } #[inline(always)] fn __stability() -> &'static ::flutter_rust_bridge::DefaultHandler<::flutter_rust_bridge::for_generated::SimpleThreadPool> { static LAZY: ::lazy_static::lazy::Lazy<::flutter_rust_bridge::DefaultHandler<::flutter_rust_bridge::for_generated::SimpleThreadPool>> = ::lazy_static::lazy::Lazy::INIT; LAZY.get(__static_ref_initialize) } __stability() } } impl ::lazy_static::LazyStatic for FLUTTER_RUST_BRIDGE_HANDLER { fn initialize(lazy: &Self) { let _ = &**lazy; } } fn wire__crate__api__simple__do_nothing_with_blob_impl(ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, data_len_: i32) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::(flutter_rust_bridge::for_generated::TaskInfo { debug_name: "do_nothing_with_blob", port: None, mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, }, move || { let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); let api_blob = >::sse_decode(&mut deserializer); deserializer.end(); transform_result_sse::<_, ()>((move || { let output_ok = Result::<_, ()>::Ok(crate::api::simple::do_nothing_with_blob(api_blob))?; Ok(output_ok) })()) }) } fn wire__crate__api__simple__init_app_impl(port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, data_len_: i32) { FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::(flutter_rust_bridge::for_generated::TaskInfo { debug_name: "init_app", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, }, move || { let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { let output_ok = Result::<_, ()>::Ok({ crate::api::simple::init_app(); })?; Ok(output_ok) })()) } }) } impl SseDecode for String { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { let mut inner = >::sse_decode(deserializer); return String::from_utf8(inner).unwrap(); } } impl SseDecode for Vec { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { let mut len_ = ::sse_decode(deserializer); let mut ans_ = ::alloc::vec::Vec::new(); for idx_ in 0..len_ { ans_.push(::sse_decode(deserializer)); } return ans_; } } impl SseDecode for u8 { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { deserializer.cursor.read_u8().unwrap() } } impl SseDecode for () { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} } impl SseDecode for i32 { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { deserializer.cursor.read_i32::().unwrap() } } impl SseDecode for bool { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { deserializer.cursor.read_u8().unwrap() != 0 } } fn pde_ffi_dispatcher_primary_impl(func_id: i32, port: flutter_rust_bridge::for_generated::MessagePort, ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len: i32, data_len: i32) { match func_id { 2 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => ::core::panicking::panic("internal error: entered unreachable code"), } } fn pde_ffi_dispatcher_sync_impl(func_id: i32, ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len: i32, data_len: i32) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { match func_id { 1 => wire__crate__api__simple__do_nothing_with_blob_impl(ptr, rust_vec_len, data_len), _ => ::core::panicking::panic("internal error: entered unreachable code"), } } impl SseEncode for String { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { >::sse_encode(self.into_bytes(), serializer); } } impl SseEncode for Vec { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { ::sse_encode(self.len() as _, serializer); for item in self { ::sse_encode(item, serializer); } } } impl SseEncode for u8 { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { serializer.cursor.write_u8(self).unwrap(); } } impl SseEncode for () { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } impl SseEncode for i32 { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { serializer.cursor.write_i32::(self).unwrap(); } } impl SseEncode for bool { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { serializer.cursor.write_u8(self as _).unwrap(); } } #[cfg(not(target_family = "wasm"))] mod io { use super::*; use flutter_rust_bridge::for_generated::byteorder::{ NativeEndian, ReadBytesExt, WriteBytesExt, }; use flutter_rust_bridge::for_generated::{ transform_result_dco, Lifetimeable, Lockable, }; use flutter_rust_bridge::{Handler, IntoIntoDart}; pub trait NewWithNullPtr { fn new_with_null_ptr() -> Self; } impl NewWithNullPtr for *mut T { fn new_with_null_ptr() -> Self { std::ptr::null_mut() } } #[no_mangle] pub extern "C" fn frb_get_rust_content_hash() -> i32 { FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH } #[no_mangle] pub extern "C" fn frb_pde_ffi_dispatcher_primary(func_id: i32, port_: i64, ptr_: *mut u8, rust_vec_len_: i32, data_len_: i32) { pde_ffi_dispatcher_primary_impl(func_id, port_, ptr_, rust_vec_len_, data_len_) } #[no_mangle] pub extern "C" fn frb_pde_ffi_dispatcher_sync(func_id: i32, ptr_: *mut u8, rust_vec_len_: i32, data_len_: i32) -> ::flutter_rust_bridge::for_generated::WireSyncRust2DartSse { pde_ffi_dispatcher_sync_impl(func_id, ptr_, rust_vec_len_, data_len_) } #[no_mangle] pub extern "C" fn dart_fn_deliver_output(call_id: i32, ptr_: *mut u8, rust_vec_len_: i32, data_len_: i32) { let message = unsafe { ::flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; FLUTTER_RUST_BRIDGE_HANDLER.dart_fn_handle_output(call_id, message) } } #[cfg(not(target_family = "wasm"))] pub use io::*; } stderr= Checking rust_lib_frb_large_transfer_hang_reproduction v0.1.0 (/home/matt/src/frb_large_transfer_hang_reproduction/rust) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/mod.rs:139] parse_function function name: "do_nothing_with_blob" [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/lifetime.rs:36] parse_function_lifetime name=do_nothing_with_blob inputs_lifetimes=[[]] output_lifetimes=[] ans=ParseFunctionLifetimeOutput { needs_extend_lifetime_per_arg: [false] } [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/ty/ty.rs:12] TypeParserWithContext.parse_type ty=u8 ans=Primitive(U8) [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/ty/ty.rs:12] TypeParserWithContext.parse_type ty=Vec < u8 > ans=PrimitiveList(MirTypePrimitiveList { primitive: U8, strict_dart_type: true }) [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/ty/ty.rs:12] TypeParserWithContext.parse_type ty=String ans=Delegate(String) [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/mod.rs:139] parse_function function name: "init_app" [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/lifetime.rs:36] parse_function_lifetime name=init_app inputs_lifetimes=[] output_lifetimes=[] ans=ParseFunctionLifetimeOutput { needs_extend_lifetime_per_arg: [] } [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/mod.rs:139] parse_function function name: "do_nothing_with_blob" [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/lifetime.rs:36] parse_function_lifetime name=do_nothing_with_blob inputs_lifetimes=[[]] output_lifetimes=[] ans=ParseFunctionLifetimeOutput { needs_extend_lifetime_per_arg: [false] } [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/ty/ty.rs:12] TypeParserWithContext.parse_type ty=u8 ans=Primitive(U8) [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/ty/ty.rs:12] TypeParserWithContext.parse_type ty=Vec < u8 > ans=PrimitiveList(MirTypePrimitiveList { primitive: U8, strict_dart_type: true }) [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/ty/ty.rs:12] TypeParserWithContext.parse_type ty=String ans=Delegate(String) [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/mod.rs:139] parse_function function name: "init_app" [2024-09-15T23:54:01.642Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/codegen/parser/mir/parser/function/real/lifetime.rs:36] parse_function_lifetime name=init_app inputs_lifetimes=[] output_lifetimes=[] ans=ParseFunctionLifetimeOutput { needs_extend_lifetime_per_arg: [] } [2024-09-15T23:54:01.643Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/dart_fix.rs:10] execute dart_fix [2024-09-15T23:54:01.643Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:129] execute command: bin=sh args="-c \"dart\" \"fix\" \"--apply\"" current_dir=Some("/home/matt/src/frb_large_transfer_hang_reproduction") cmd=cd "/home/matt/src/frb_large_transfer_hang_reproduction" && "sh" "-c" "\"dart\" \"fix\" \"--apply\"" [2024-09-15T23:54:03.224Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:140] command=cd "/home/matt/src/frb_large_transfer_hang_reproduction" && "sh" "-c" "\"dart\" \"fix\" \"--apply\"" stdout=Computing fixes in frb_large_transfer_hang_reproduction... Nothing to fix! stderr= [2024-09-15T23:54:03.225Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/dart_format.rs:23] execute dart_format paths=["lib/src/rust/frb_generated.web.dart", "lib/src/rust/frb_generated.dart", "lib/src/rust/api/simple.dart", "lib/src/rust/frb_generated.io.dart"] line_length=80 [2024-09-15T23:54:03.225Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:129] execute command: bin=sh args="-c \"dart\" \"format\" \"--line-length\" \"80\" \"lib/src/rust/frb_generated.web.dart\" \"lib/src/rust/frb_generated.dart\" \"lib/src/rust/api/simple.dart\" \"lib/src/rust/frb_generated.io.dart\"" current_dir=Some("/home/matt/src/frb_large_transfer_hang_reproduction") cmd=cd "/home/matt/src/frb_large_transfer_hang_reproduction" && "sh" "-c" "\"dart\" \"format\" \"--line-length\" \"80\" \"lib/src/rust/frb_generated.web.dart\" \"lib/src/rust/frb_generated.dart\" \"lib/src/rust/api/simple.dart\" \"lib/src/rust/frb_generated.io.dart\"" [2024-09-15T23:54:03.678Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:140] command=cd "/home/matt/src/frb_large_transfer_hang_reproduction" && "sh" "-c" "\"dart\" \"format\" \"--line-length\" \"80\" \"lib/src/rust/frb_generated.web.dart\" \"lib/src/rust/frb_generated.dart\" \"lib/src/rust/api/simple.dart\" \"lib/src/rust/frb_generated.io.dart\"" stdout=Formatted lib/src/rust/frb_generated.web.dart Formatted lib/src/rust/frb_generated.dart Formatted lib/src/rust/api/simple.dart Formatted lib/src/rust/frb_generated.io.dart Formatted 4 files (4 changed) in 0.18 seconds. stderr= [2024-09-15T23:54:03.678Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/format_rust.rs:9] execute format_rust paths=["src/frb_generated.rs"] [2024-09-15T23:54:03.678Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:129] execute command: bin=sh args="-c \"rustfmt\" \"--edition\" \"2018\" \"src/frb_generated.rs\"" current_dir=Some("/home/matt/src/frb_large_transfer_hang_reproduction/rust") cmd=cd "/home/matt/src/frb_large_transfer_hang_reproduction/rust" && "sh" "-c" "\"rustfmt\" \"--edition\" \"2018\" \"src/frb_generated.rs\"" [2024-09-15T23:54:03.715Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/commands/command_runner.rs:140] command=cd "/home/matt/src/frb_large_transfer_hang_reproduction/rust" && "sh" "-c" "\"rustfmt\" \"--edition\" \"2018\" \"src/frb_generated.rs\"" stdout= stderr= [2024-09-15T23:54:03.715Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/utils/dart_repository/dart_repo.rs:21] Guessing toolchain the runner is run into [2024-09-15T23:54:03.715Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/utils/dart_repository/dart_repo.rs:68] Checking presence of flutter_rust_bridge in dependencies at "/home/matt/src/frb_large_transfer_hang_reproduction" [2024-09-15T23:54:03.715Z DEBUG /home/matt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flutter_rust_bridge_codegen-2.4.0/src/library/utils/dart_repository/dart_repo.rs:93] Checking presence of flutter_rust_bridge in dependencies at "/home/matt/src/frb_large_transfer_hang_reproduction" [0.2s] Parse └── [0.2s] Cargo expand & syn parse └── [0.0s] Parse HIR └── [0.0s] Parse MIR [0.0s] Generate [2.1s] Polish └── [1.6s] Run Dart fix └── [0.5s] Run Dart formatter └── [0.0s] Run Rust formatter └── [0.0s] Auto upgrade Done! ```

Expected behavior

I expected the UInt8List to transparently be transferred to the Rust worker using some zero-copy efficient magic.

Generated binding code

I don't know what this field is referring to.

OS

Linux, Debian Testing

Version of flutter_rust_bridge_codegen

2.4.0

Flutter info

[✓] Flutter (Channel stable, 3.24.3, on Debian GNU/Linux trixie/sid 6.10.6-amd64, locale en_CA.UTF-8)
    • Flutter version 3.24.3 on channel stable at /home/matt/local/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 2663184aa7 (4 days ago), 2024-09-11 16:27:48 -0500
    • Engine revision 36335019a8
    • Dart version 3.5.3
    • DevTools version 2.37.3

[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/to/linux-android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.

[✓] Chrome - develop for the web
    • CHROME_EXECUTABLE = chromium

[✓] Linux toolchain - develop for Linux desktop
    • Debian clang version 15.0.7
    • cmake version 3.30.3
    • ninja version 1.12.1
    • pkg-config version 1.8.1

[!] Android Studio (not installed)
    • Android Studio not found; download from https://developer.android.com/studio/index.html
      (or visit https://flutter.dev/to/linux-android-setup for detailed instructions).

[✓] VS Code (version 1.93.1)
    • VS Code at /usr/share/code
    • Flutter extension version 3.97.20240902

[✓] Connected device (2 available)
    • Linux (desktop) • linux  • linux-x64      • Debian GNU/Linux trixie/sid 6.10.6-amd64
    • Chrome (web)    • chrome • web-javascript • Chromium 128.0.6613.113 built on Debian GNU/Linux trixie/sid

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 2 categories.

Version of clang++

15.0.7

Additional context

No response

welcome[bot] commented 2 months ago

Hi! Thanks for opening your first issue here! :smile:

fzyzcjy commented 2 months ago

Hmm, 1MB/s looks really slow. As a first sanity check, could you please log::info!("current time is: ..."); and print current time inside do_nothing_with_blob, and also print time in _processBlob, to see whether it is really the time there.

In addition, is your flutter/dart compiled to js, or compiled to wasm (the newly supported one)? If wasm, could you please also try to run js

If the problem still exists, I will work on it soon (within several days or a week; ping me if I forget)

isosphere commented 2 months ago

Hmm, 1MB/s looks really slow. As a first sanity check, could you please log::info!("current time is: ..."); and print current time inside do_nothing_with_blob, and also print time in _processBlob, to see whether it is really the time there.

  Future<String?> _processBlob() async {
    print("Making blob.");
    Uint8List blob = Uint8List(1024 * 1024 * 10); // 10MB
    print("Blob made, sending to Rust."); // this occurs immediately
    print("The current epoch time is: ${DateTime.now().millisecondsSinceEpoch/1000}"); 
    return doNothingWithBlob(blob: blob); // this hangs the UI for about 10 seconds
  }
#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn do_nothing_with_blob(blob: Vec<u8>) -> String {
    info!(format!("called at {:?}", SystemTime::now()));
    format!("Hello, I didn't even look at the blob! Nyah!")
}

Making blob. js_primitives.dart:28 Blob made, sending to Rust. js_primitives.dart:28 The current epoch time is: 1726448144.222 rust_lib_frb_large_transfer_hang_reproduction.js:593 called at SystemTime(1726448151.985s)

1726448151.985 - 1726448144.222 = 7.76 seconds for 1024 1024 10 bytes; 1.35 MB/s

In addition, is your flutter/dart compiled to js, or compiled to wasm (the newly supported one)? If wasm, could you please also try to run js

I think it's JS, but I'm new to flutter. Here's my run command:

flutter run -d chrome --web-port=8080 --web-hostname=hostname --web-header=Cross-Origin-Opener-Policy=same-origin --web-header=Cross-Origin-Embedder-Policy=require-corp

isosphere commented 2 months ago

Interestingly, if I build my Rust WASM blob in release mode, the data transfers at 228 MB/s.

Here's the command I used for that:

flutter_rust_bridge_codegen build-web --release

So we at least know that this is confined to debug WASM binaries. I'm not sure how to debug a Rust WASM binary, so maybe that's fine? Is it valuable to support quick transfer in debug binaries? 🤷‍♀️

fzyzcjy commented 2 months ago

if I build my Rust WASM blob in release mode, the data transfers at 228 MB/s.

Aha! Yes you should always test any performance using release mode instead of debug mode. It is almost always the case that debug mode is far far slower.

For example, Flutter itself is also very slow when you use debug mode (e.g. a larger app in debug mode will be quite janky); but when in release/profile mode, the app is quite fast and smooth.

Btw you may also build your flutter code in release mode (in addition to Rust in release mode) to test performance.

github-actions[bot] commented 1 month ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new issue.