fireproofsocks

fireproofsocks

How to leverage concurrency in Enum.reduce()?

This may be a basic question, but it’s not always clear to me when or how one should leverage Elixir’s concurrency support.

Consider a simple example where we want to replace every vowel in a sentence with its upper-case equivalent. The replacements list contains tuples indicating the character to find and its replacement:

original = "this is a sentence originally all lower-case"
      replacements = [
        {"a", "A"},
        {"e", "E"},
        {"i", "I"},
        {"o", "O"},
        {"u", "U"},
      ]
      
      output = Enum.reduce(replacements, original, fn {find, replacement}, acc ->
          String.replace(acc, find, replacement) # <-- works
      end)

This works, but it does not leverage concurrency. I tried to use spawn(fn -> String.replace(acc, find, replacement) end) inside the reduce function, but of course that doesn’t work because spawn() returns a PID.

This problem is probably a poor candidate for concurrency because each replacement relies on the output of the previous operation. Is there a way to structure this problem in a way that might take advantage of concurrent processes?

Thanks for any pointers!

Most Liked

kip

kip

ex_cldr Core Team

A recursive function with binary comprehensions will be about twice as fast as your current approach (but not as generalised):

  def replace(<< "" >>), do: ""
  def replace(<< "a", rest :: binary >>), do: << "A", replace(rest) :: binary >>
  def replace(<< "e", rest :: binary >>), do: << "E", replace(rest) :: binary >>
  def replace(<< "i", rest :: binary >>), do: << "I", replace(rest) :: binary >>
  def replace(<< "o", rest :: binary >>), do: << "O", replace(rest) :: binary >>
  def replace(<< "u", rest :: binary >>), do: << "U", replace(rest) :: binary >>
  def replace(<< c :: utf8, rest :: binary >>), do: << c, replace(rest) :: binary >>

To approach concurrency I expect you would need to:

  1. Chunk the string into substrings
  2. Use Task.async_stream/4 to run your replacement algorithm concurrently
  3. And then join the fragments back up.:
max_concurrency = System.schedulers_online() * 2
stream = Task.async_stream(chunks, MyModule, :replace, [], max_concurrency: max_concurrency, ordered: true)

stream
|> Enum.to_list
|> Enum.join

Unless the string is of reasonable length, I suspect the cost of chunking the string (accounting for unicode would be more expensive than assuming ascii) and then creating the stream and rejoining the chunks would create more overhead than you would save. Thats more string allocation, and also counting and splitting strings can be relatively expensive. I expect for your sample sentence it wouldn’t be worth it.

I would take some experimentation to find the optimal choice between serial and concurrent, how many concurrent streams, how long the string, whether you support unicode (you should) and so on.

kokolegorille

kokolegorille

As You mentionned reduce is not a good candidate because elements are dependant. Recursive functions as well… But if elements are independants, that could be valuable.

An example would be map and pmap :slight_smile:

Where Next?

Popular in Questions Top

Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Kagamiiiii
Student &amp; New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
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
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
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
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

Other popular topics Top

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
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
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
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement