<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="135990" data-post-id="135990">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="OvermindDL1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/OvermindDL1/120/2677_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  OvermindDL1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="bglusman" data-post="50" data-topic="23853">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bglusman/48/9459_2.png" class="avatar"> bglusman:</div>
<blockquote>
<p>True, but thats an acknowledged limitation… though I suppose that might be a good additional option that wouldn’t require the option_map, but different time/space complexity tradeoffs, the overhead of rescuing when wrong is a lot higher than nil map lookup but agree its more reliable and less memory intensive… <img src="https://forum.elixirforum.com/images/emoji/apple/man_shrugging.png?v=15" title=":man_shrugging:" class="emoji" alt=":man_shrugging:" loading="lazy" width="20" height="20"> nothing wrong with either but there’s some overhead for using this and didn’t want ti to be too terrible but maybe that’s the new third config option!</p>
</blockquote>
</aside>
<p>It can potentially be a <em>lot</em> of memory though, tens of thousands to even hundreds of thousands of atoms on some systems, plus you are copying that entire massive map on every call to <code>Application.get_env</code> since it’s not static (a persistent term would fix that though, but still eat memory, and no point putting atoms in the persistent term registery directly as that’s just duplicating the atom registry).</p>
<aside class="quote no-group" data-username="benwilson512" data-post="51" data-topic="23853" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/benwilson512/48/1457_2.png" class="avatar"> benwilson512:</div>
<blockquote>
<p>Sounds like it’s time to break out <code>benchee</code></p>
</blockquote>
</aside>
<p>Did you call?  ^.^</p>
<pre><code class="lang-plaintext">╰─➤  tail -n 22 bench/atom_map_or_existing_bench.exs 

  def actions(_cla, _setup),
    do: %{
          "map_access" =&gt; fn key -&gt;
            atom = atoms_map()[key]
            if atom, do: atom, else: key
          end,
          "map_get" =&gt; fn key -&gt;
            Map.get(atoms_map(), key, key)
          end,
          "persistent_mapterm_access" =&gt; fn key -&gt;
            Map.get(:persistent_term.get(:all_atoms_map), key, key)
          end,
          "to_existing" =&gt; fn key -&gt;
            try do
              String.to_existing_atom(key)
            rescue ArgumentError -&gt;
              key
            end
          end,
    }
end

╰─➤  mix bench atom_map_or_existing
Operating System: Linux"
CPU Information: AMD Phenom(tm) II X6 1090T Processor
Number of Available Cores: 6
Available memory: 15.67 GB
Elixir 1.8.1
Erlang 21.2.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 2 s
memory time: 2 s
parallel: 1
inputs: $blah, true
Estimated total run time: 48 s


Benchmarking map_access with input $blah...
Benchmarking map_access with input true...
Benchmarking map_get with input $blah...
Benchmarking map_get with input true...
Benchmarking persistent_mapterm_access with input $blah...
Benchmarking persistent_mapterm_access with input true...
Benchmarking to_existing with input $blah...
Benchmarking to_existing with input true...

##### With input $blah #####
Name                                ips        average  deviation         median         99th %
persistent_mapterm_access        7.98 M       0.125 μs    ±16.96%       0.130 μs        0.22 μs
to_existing                      4.45 M        0.22 μs   ±119.53%        0.21 μs        0.33 μs
map_get                       0.00118 M      846.05 μs    ±75.88%         478 μs     2972.98 μs
map_access                    0.00113 M      886.78 μs    ±87.96%         488 μs     3203.40 μs

Comparison: 
persistent_mapterm_access        7.98 M
to_existing                      4.45 M - 1.79x slower
map_get                       0.00118 M - 6753.50x slower
map_access                    0.00113 M - 7078.61x slower

Memory usage statistics:

Name                         Memory usage
persistent_mapterm_access           136 B
to_existing                         136 B - 1.00x memory usage
map_get                             192 B - 1.41x memory usage
map_access                          192 B - 1.41x memory usage

**All measurements for memory usage were the same**

##### With input true #####
Name                                ips        average  deviation         median         99th %
persistent_mapterm_access        7.29 M       0.137 μs    ±12.94%       0.130 μs        0.20 μs
to_existing                      7.28 M       0.137 μs    ±11.35%       0.130 μs        0.20 μs
map_access                    0.00116 M      859.70 μs    ±78.67%         474 μs     3089.70 μs
map_get                       0.00116 M      865.42 μs    ±77.40%         474 μs     3133.01 μs

Comparison: 
persistent_mapterm_access        7.29 M
to_existing                      7.28 M - 1.00x slower
map_access                    0.00116 M - 6265.69x slower
map_get                       0.00116 M - 6307.36x slower

Memory usage statistics:

Name                         Memory usage
persistent_mapterm_access           136 B
to_existing                         136 B - 1.00x memory usage
map_access                          192 B - 1.41x memory usage
map_get                             192 B - 1.41x memory usage

**All measurements for memory usage were the same**

</code></pre>
<p>(I do an assert to verify <code>$blah</code> doesn’t exist as an atom of course)</p>
<p>In all cases the map from the application environment is the slowest by <em>monumental</em> amounts.</p>
<p>When the atom <strong>does</strong> exist then then the persistent mapterm and the <code>to_existing_atom</code> are about the same speed.</p>
<p>When the atom does <strong>NOT</strong> exist then the <code>to_existing _atom</code> is a bit better than half the speed of the persistent mapterm, which at those speeds (millions per second) is not worth caring about.</p>
<p><em>/me loves benchmarks</em></p>
<p>EDIT:  The <code>atoms_map()</code> and the <code>initialize_atoms_map()</code> calls are just copy/pasted from this IndifferentAccess module and initialized only once on <code>setup</code>.  ^.^</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="135990" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/indiffererentaccess-adaptation-of-hashwithindifferentaccess-to-elixir-maps-plug/23853/52">Post #51</a>
	                </div>
	            </div>
              <div id="likers-container-135990" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="135990"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #51"></div>
  </section>
</div>
    <div class="postbit" id="136151" data-post-id="136151">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="bglusman" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bglusman/120/9459_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  bglusman
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Awesome!  I’ll kill the map then and do a rescue! Thanks for running that <a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> and thanks for suggesting <a class="mention" href="/u/benwilson512" rel="nofollow">@benwilson512</a>!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="136151" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/indiffererentaccess-adaptation-of-hashwithindifferentaccess-to-elixir-maps-plug/23853/53">Post #52</a>
	                </div>
	            </div>
              <div id="likers-container-136151" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="136151"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #52"></div>
  </section>
</div>
    <div class="postbit" id="136515" data-post-id="136515">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="bglusman" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bglusman/120/9459_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  bglusman
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks again for the Benchee stats!  Hex and GIthub are updated, no longer using atoms_map!  Good reminder to always benchmark to test assumptions!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="136515" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/indiffererentaccess-adaptation-of-hashwithindifferentaccess-to-elixir-maps-plug/23853/54">Post #53</a>
	                </div>
	            </div>
              <div id="likers-container-136515" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="136515"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #53"></div>
  </section>
</div>
    <div class="postbit" id="281769" data-post-id="281769">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jtompl" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jtompl/120/10019_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jtompl
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>For anyone meeting this problem, we’ve just published <a href="https://github.com/allegro/map-with-indifferent-access" rel="noopener nofollow ugc">map_with_indifferent_access</a> library.</p>
<p>It gives you functions like <code>MapWithIndifferentAccess.get/3</code> <code>MapWithIndifferentAccess.put/3</code>, that mimic <code>Map</code> API, but:</p>
<ol>
<li>
<p>You need to use atom keys when calling the functions. (Even if the map uses string keys.)</p>
</li>
<li>
<p>If the map uses string keys, <code>key</code> argument will be converted to a string, and only then called with a respective <code>Map</code> function.</p>
</li>
</ol>
<p>For guessing whether the map uses string or atom keys, it looks only at a key of random element of it. It does it the same way Ecto <a href="https://github.com/elixir-ecto/ecto/blob/v3.8.4/lib/ecto/changeset.ex#L613" rel="noopener nofollow ugc">currently</a> does it.</p>
<p>We find it useful when working with <code>Ecto.Changeset</code> , as <a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#cast/4" rel="noopener nofollow ugc"><code>Ecto.Changeset.cast/4</code></a> forbids passing maps with mixed key types.</p>
<p>For example, we have a domain layer code (that uses ecto changesets underneath), that’s called either by a controller (with string keyed maps) or by some other domain layer code (with atom keyed maps).</p>
<p>The <code>MapWithIndifferentAccess</code> allow us to interact with the map (e.g. get or put elements of it), without having to “guess” if it currently uses string or atom keys.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="281769" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/indiffererentaccess-adaptation-of-hashwithindifferentaccess-to-elixir-maps-plug/23853/55">Post #54</a>
	                </div>
	            </div>
              <div id="likers-container-281769" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="281769"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>