Desempenho do mutlitheading em RX vs Theads vs Executors

8

Estou escrevendo um aplicativo de back-end no Kotlin.

Para acelerar, atualmente estou confiando no RxKotlin no servidor para executar paralelamente tarefas de E / S, como chamadas de banco de dados e API. O código geralmente se parece com isso.

val singleResult1 = Single.fromCallable{
  database.get(....)
}.io()

val singleResult2 = Single.fromCallable{
  database.update(....)
}.io()

Single.zip(singleResult1, singleResult2){ result1: Result1, result2: Result2 ->
    ....
}
.flatMap{
  //other RX calls
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.blockingGet()

No entanto, como o trabalho não funciona realmente com vários eventos (apenas singles), o Rx se sente um pouco confuso e apenas adiciona um monte de clichê (também causa complicações se eu quiser retornar um valor nulo e, às vezes, atrapalhar o rastreamento da pilha )

Estou pensando em remover Rx e usar Executors(ou threads) para paralelismo insteada. Existem considerações de desempenho a serem consideradas aqui?

Exemplo no que estou pensando:

fun <T> waitAll(tasks: List<Callable<T>>, threadCount: Int = -1): List<T> {
    val threads = if (threadCount == -1) tasks.size else threadCount
    val executor = Executors.newFixedThreadPool(threads)
    val results = executor.invokeAll(tasks).map {
        it.get()
    }
    executor.shutdown()
    return results
}

E usando-o assim:

waitAll(listOf(callable1, callable2))

Ou talvez usando threads regulares e se juntar a eles?

threads.forEach{
   it.start()
}
threads.forEach{
   it.join()
}

Ou por que não fluxos?

listOf(callable1,callable2)
.parallelStream()
.map{it.call()}
.collect(Collectors.toList())
Richard
fonte
1
o que você quer dizer com: Rx se sente um pouco confuso e apenas adiciona um monte de clichê (isso também causa complicações se eu quiser retornar um valor nulo e às vezes pode atrapalhar o rastreamento da pilha) ?
bubbles
1
Pelo amor de Deus, não siga a rota do fio. Você deseja ter uma abstração de tarefa adequada e os threads ficam muito confusos muito rapidamente. A rota do fluxo parece mais promissora se você não encontrar nenhuma limitação. Eu provavelmente substituiria o RxKotlin por corotinas se as coisas ficarem mais complicadas: ele tem o mesmo escopo do RxKotlin e não se limita à interface do usuário como você escreveu em outro lugar, mas parece mais idiomático.
Arvid Heise

Respostas:

4

O próprio KotlinRx usa executores, exceto que ele possui dois conjuntos de threads pré-criados em Schedulers.io()(ilimitado) e Schedulers.computation()(limitado pelo número de núcleos) e não cria um novo a cada vez, como o código sugerido. O que você obviamente também pode fazer manualmente:

private val executor = Executors.newCachedThreadPool() // equivalent to io()

fun <T> waitAll(tasks: List<Callable<T>>): List<T> {
    return executor.invokeAll(tasks).map {
        it.get()
    }
}

Isso deve ser melhor do que criar um encadeamento para cada tarefa, geralmente falando, permitindo reutilizar encadeamentos existentes, mas depende do seu uso.

coroutines é (IMO) mais adequado para lidar com a comunicação de segundo plano / thread de interface do usuário (isto é, Android, etc.)

Se as corotinas são úteis para isso depende muito do que você tem em suas tarefas. Uma API de bloqueio (por exemplo, JDBC)? Eles não são. Um assíncrono (por exemplo, Retrofit)? Eles são.

Alexey Romanov
fonte
5

Os serviços Java Executor usam Threads e RxKotlin usa ExecutorServices. Então, todos esses são os mesmos em segundo plano. A diferença é arquitetura de software. Portanto, se você escolher a melhor arquitetura para ser integrada ao seu código, ele funcionará melhor e fará o trabalho corretamente. O simples é o melhor.

Se você tiver uma arquitetura baseada em evento ou observável e estiver tentando implementar uma nova biblioteca para trabalhar com operações ou trabalhos com base em eventos, poderá escrever etapas erradas sobre separação ou tempo de trabalho. Use RxKotlin e não invente a roda novamente.

Se o seu trabalho não for sobre eventos ou padrão observável e você apenas precisar executar tarefas paralelas, use os serviços do Executor. O RxKotlin será sobre engenharia. Quando você usa o RxKotlin nessa situação, precisa fazer mais coisas do que precisa.

Então eu acho que a questão não é a velocidade nessa situação, a arquitetura é.

Burak Akyıldız
fonte