Introdução
Depois de contribuir para um package da comunidade(por ex. flutter_zxing) tive a curiosidade de me aprofundar mais em como chamar código nativo direto de um projeto Dart, e também estou tendo cada vez mais interesses em linguagens como Rust e Golang, elas me atraem como o mel atraem ursos 🍯.
Então me propus a usar Golang interpolado com Dart e trouxe aqui um pequeno guia para você desenvolvedor e sofredor.
Antes de tudo é bom ter o Golang corretamente configurado em seu ambiente de desenvolvimento, junto também o C.
Golang com Dart 🤝
Escrevi um código Golang básico que calcula a sequência fibonacci. Note para a importação import "C"
, o cgo é o responsável pela ajuda de chamar código C com Golang.
package main
import "C"
//export fibonacci
func fibonacci(n C.uint) C.uint {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {}
Indicamos a função a ser exportada usando o comentário //export nomefuncao
como documentado aqui
Para usar o ffigen devemos gerar o build e ter no minimo o arquivo de cabeçalho C e o arquivo de objeto compartilhado, os famosos *.so
compilados. Como vou usar o flutter no Android, usei o comando:
CGO_ENABLED=1 GOOS="android" GOARCH="amd64" GOARM="" go build -buildmode=c-shared -o lib.so fibonacci.go
// para ver as possibilidades rode o comando `$ go tool dist list`
Talvez você precise apontar para para o NDK adicionando
CC=meu caminhoparandk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang
Criando um projeto Flutter e tendo adicionado o ffigen as dependências de desenvolvimento podemos através do arquivo de cabeçalho gerar nosso código Dart para interpolar tudo isso.
flutter pub add -d ffigen
Para configurar o ffigen basta seguir a documentação aqui. Nesse nosso projeto adicionamos o seguinte ao final do pubspec.yaml:
# Meu sistema é um Linux Fedora
ffigen:
name: "FibonacciGO"
description: "Bindings to FibonacciGO"
output: "lib/generated_bindings.dart"
llvm-path:
- "/usr/local/opt/llvm"
- 'C:\Program Files\llvm'
- "/usr/lib/llvm-11"
- "/usr/lib64/libclang.so"
headers:
entry-points:
- "lib.h"
Para gerar os binds basta executar o comando abaixo e o arquivo indicado no pubspec.yaml
será gerado
flutter pub run ffigen
E para facilitar criei um arquivo para facilitar o bind
import 'dart:ffi';
import 'dart:io';
import 'package:fluttergo/generated_bindings.dart';
final FibonacciGO fibonacciGO = FibonacciGO(dylib);
DynamicLibrary _openDynamicLibrary() {
if (Platform.isAndroid) {
return DynamicLibrary.open('lib.so');
} else if (Platform.isLinux) {
return DynamicLibrary.open('/dev/blog/fluttergo/lib.so');
} else if (Platform.isWindows) {
return DynamicLibrary.open('lib.dll');
}
return DynamicLibrary.process();
}
DynamicLibrary dylib = _openDynamicLibrary();
Caso tenha gerado os arquivos
*.so
para Android copie e cole cada um na respectiva pasta da arquitetura
- android/app/src/main/jniLibs/arm64-v8a
- android/app/src/main/jniLibs/armeabi-v7a
- android/app/src/main/jniLibs/x86
- android/app/src/main/jniLibs/x86_64
Performance 🏇
Criei uma função com a mesma implementação fibonacci em Dart. Usei o benchmark_harness
para medir o desempenho Dart x Golang interpolado obtive o mesmo desempenho.
int fibonacci(int n) => n <= 2 ? 1 : fibonacci(n - 2) + fibonacci(n - 1);
Porém otimizando o código Golang o trem decolou
//export fibonacciSequential
func fibonacciSequential(n C.uint) C.uint {
if n <= 1 {
return C.uint(n)
}
var n2, n1 C.uint = 0, 1
for i := C.uint(2); i < n; i++ {
n2, n1 = n1, n1+n2
}
return n2 + n1
}
flutter: Run(RunTime): 388.61892877711654 us. //Dart
flutter: Run(RunTime): 2.5107325 us. //Golang
*µs => microsegundos
O código abaixo alcançou flutter: Run(RunTime): 0.3191476466012018 us.
. Em contra partida não tentei mais otimizar o código Golang.
int fibonacciInLoops(int n) {
if (n < 2) return n;
var data = [0, 1];
for (var i = 2; i < n + 1; i++) {
data.add(data[i - 1] + data[i - 2]);
}
return data[data.length - 1];
}
Isso quer dizer que é muito mais provável que somente seja viável usar interpolação caso precise de grande poder computacional, grande volume de dados a ser tratados, algum código nativo especifico ou alguma solução em outra linguagem.
Considerações ✍️
Caso precise de um poder computacional grande talvez seja necessário interpolar com código nativo usando Golang, Rust, Java ou até C puro - tratamento de imagens e vídeos, criptografia, compressão de arquivos, IA, etc.
Repositório para você conferir