brendan-duncan / image

Dart Image Library for opening, manipulating, and saving various different image file formats.
MIT License
1.14k stars 255 forks source link

Release build much slower than debug build when running on isolates with Command.executeThread() on Android #660

Open eduribas opened 2 weeks ago

eduribas commented 2 weeks ago

The following simple code runs much slower in release build than in debug build on Android:

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image/image.dart' as im;

void main() {
  runApp(const MainApp());
}

class MainApp extends StatefulWidget {
  const MainApp({super.key});

  @override
  State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  Duration? _elapsedTime;
  bool _running = false;

  void _processPicture() async {
    final file = await ImagePicker().pickImage(source: ImageSource.gallery);
    if (file != null) {
      setState(() {
        _running = true;
      });
      final bytes = await file.readAsBytes();
      final command = im.Command()
        ..decodeImage(bytes)
        ..encodeJpg();
      final startTime = DateTime.now();
      await command.executeThread();
      setState(() {
        _running = false;
        _elapsedTime = DateTime.now().difference(startTime);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              if (_running)
                const CircularProgressIndicator()
              else
                Text(_elapsedTime != null
                    ? 'Elapsed milliseconds: ${_elapsedTime!.inMilliseconds}'
                    : ''),
              OutlinedButton(
                onPressed: _processPicture,
                child: const Text('Select picture'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
eduribas commented 1 week ago

By further testing I noticed that even if not running in isolates, by changing await command.executeThread(); to await command.execute(); in the code above, it still runs around 25% slower in release builds than in debug builds, in a Pixel 7 Pro.

brendan-duncan commented 1 week ago

This kinda feels like a Dart issue, I'm not sure what I'd be able to do about it. It must be hitting some performance penalty case for Dart release optimizations. I agree it's a terrible performance loss, and completely backwards to what you would expect.

eduribas commented 1 week ago

This kinda feels like a Dart issue, I'm not sure what I'd be able to do about it. It must be hitting some performance penalty case for Dart release optimizations. I agree it's a terrible performance loss, and completely backwards to what you would expect.

Do you think it is related to this flutter issue: https://github.com/flutter/flutter/issues/140351 ?

As discussed in that issue, the usage of num in computations is really problematic for the AOT compiler used for release builds.

brendan-duncan commented 1 week ago

I can take a look at num usage when I get a chance.

brendan-duncan commented 1 week ago

I suspect there is is something similar going on, but I don't know if it's num causing the performance hit with the AOT release build. num isn't used in the decoders and encoders.