eyelight93

eyelight93

How to leverage concurrency and speed up the aggregate operation

Hi guys, I’m having this problem.
Let say I have a list of 10_000 items, each item is a map with a RRule column

  [
     %{rrules: 
        [
           %{value: 1, rule: rrule}, 
           %{value: 2, rule: rrule2}
        ]
     },
     ........
  ]

I’m spawning 10_000 tasks, each will handle one item

Enum.map(list, fn item -> Task.async(fn -> handle_item(item.rrules) end) end)
|> Enum.reduce(%{}, fn task, acc -> 
  value = Task.await(task)
  Map.put(acc, value, 1)
end)

handle_item/1 will look like this

Enum.map(rrules, fn element -> 
  element.rule
  |> CockTail.occurrences() # This returns a stream of time-series data ~ 8000 records
  |> Enum.reduce(%{}, fn time, acc -> Map.put(acc, time, element.value) end)
end)

obviously, this implementation is very inefficient. But I’m not sure how I can leverage the concurrent in this case, to speed up the aggregate operation.
I would be very appreciated if somebody could point me to the right direction, thanks in advance

Most Liked

kip

kip

ex_cldr Core Team

I’d consider using Task.async_stream/2 or Flow for this first part. I believe it will do a better job on managing throughput.

I assume this isn’t your real code because here you are using the map returned from handle_item/1 as a map key.

You could consider using an :ets table for the accumulator since that allows concurrent write access. However concurrency here isn’t a magic bullet since you are already running this code pithing a concurrent environment (the original 10_000 items).

This looks like you are exploding an rrule into individual event times (is that what CockTail.occurrences/1 does?). I think the key question here is whether you need to materialise all the times up front or whether there is an opportunity to lazily evaluate the individual times when you need them. The calendar_recurrence might help with this.

kip

kip

ex_cldr Core Team

I think switching the Flow would be your best bet since it is designed for exactly this kind of use case. Something like (not tested):

items()
|> Flow.from_enumerable(max_demand: :erlang.system_info(:schedulers_online))
|> Flow.map(&handle_item/1))
|> Flow.partition()
|> Flow.reduce(fn -> %{} end, fn time, acc -> Map.put(acc, time, element.value) end)
|> Enum.to_list()
dimitarvp

dimitarvp

10k items? This should barely blip for 0.5 secs IMO. If you can put some example GitHub project that can produce the items so we can check a few algorithms, we should be able to help further.

kip

kip

ex_cldr Core Team

I suspect the time is taken materialising the time events (the code says about 8,000 of them for each of the 10,000 items). Avoiding materialising these time events would, I think, be the better path.

mudasobwa

mudasobwa

Creator of Cure

+1 for leveraging Flow. It’s literally designed to solve tasks like that one.

Where Next?

Popular in Questions Top

Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New

Other popular topics Top

chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New

We're in Beta

About us Mission Statement