On 10/27/22 3:42 PM, José Valim wrote:

> 1. Maps are slower (significantly enough to avoid IMHO), but do give the flexibility desired (1.79x slower, which could compound if in the wrong place)

Do you have a source for the 1.79 slower?

I don't expect maps to be slower but, more importantly, the difference here should be negligible because options rarely exceed more than a dozen keys and in those scenarios lookups are in the order of single-digit microseconds (source <https://github.com/erlang/otp/issues/6139#issuecomment-1182995087>) *in the worst case*. So it seems counter productive to say you wouldn't pick the more flexible option because an operation will take 1.75us instead of 1us (assuming it is indeed slower). :)

Plus maps have other benefits such as reading multiple keys at once which will certainly offset keyword lists.

1.79 times, as I read it, not 1.79us. And of course benchmarks being highly subjective, now that I retooled it it's at 2.12x slower (see notes at the very bottom for likely reasons why).

What drove me to look into this is a very specific situation we face in my project where we process a high frequency of events on a databus per second, and it was bogging down. As I isolated easy wins (external database calls) I started looking into other inefficiencies.

Specifically this is a call that looks at the before and after state of a struct and from that decides if changes should be handled or not.

I expect many will likely find fault with the implementation of this benchmark, but it is for a specific use case... for the room in general: let's not digress on if there are better ways to do a thing or not :)

The gist includes three scenarios:

 * ordered — arguments are given in order and pipelined (this is what
   we are using now)
 * map_get — arguments are culled ala Map.take()
 * map_pattern — arguments use a pattern match w/a dictionary

The #'s:

Name                  ips        average deviation         median         99th % ordered            5.06 M      197.65 ns ±21980.53%         143 ns         230 ns map_get            3.86 M      259.27 ns ±28310.93%         183 ns         494 ns map_pattern        2.39 M      418.72 ns ±11140.31%         426 ns         551 ns

Comparison:
ordered            5.06 M
map_get            3.86 M - 1.31x slower +61.61 ns
map_pattern        2.39 M - 2.12x slower +221.07 ns


The gist / benchmark that created the above: https://gist.github.com/srevenant/ed28a0cf2a167379a21eb482291313ce


Why the change?

I did change it from when I ran it previously to come up with the 1.79x number, because it was referencing our code base, and in the previous benchmark I had less arguments it was matching against. I added a few more arguments to amplify the situation, as you can see.

I do realize this is NOT the heart of my performance problem, so I hope others can avoid digressing into "you should do it this other way" type discussions :D

This is just something that came out of exploring some performance issues.

And the suggestion for efficient named arguments came because we'd switched to ordered arguments, and then the code was flipped in one place (a, b) vs (b, a) (face-palm)

-Brandon

--
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/f3307a40-b4fe-c66f-0576-a162202dfee2%40cold.org.

Reply via email to