nanoid icon indicating copy to clipboard operation
nanoid copied to clipboard

Use Application.get_env/3 calls instead of module attributes

Open frm opened this issue 4 years ago • 6 comments

Module attributes are defined in compile time. I just happened to download and compile the application (which includes compiling NanoID as a dependency) and then change the config in my app to use a different size by default.

However, since the @default_size module attribute was defined when NanoID compiled, it wasn't having any effect. I had to explicitly recompile NanoID.

Instead of doing so, we can define the module attribute to be the fallback default value and use Application.get_env/3 in runtime to get the value from the application config.

Note: I made this separate to #7 since this is an issue that might not necessarily be merged.

frm avatar Jul 07 '20 16:07 frm

This was actually the main reason why I introduced the Configuration module. However, it turnes out that continuously reading the configuration via Application.get_env/3 has a significant effect on the speed of nanoid. In my tests, it lowers the generation speed (ips) about 1k. Please see #6 for further information. For this reason I have prioritized speed.

But you can recompile nanoid with mix deps.compile nanoid --force when the configuration has changed. For the deployment, I used to compile everything in a fresh docker container to avoid such problems.

railsmechanic avatar Jul 07 '20 17:07 railsmechanic

Ah, I missed that. My bad. That's actually really interesting. It would probably help other developers to be aware of this limitation. Would you consider adding a note referencing/explaining this in the configuration section of the README?

frm avatar Jul 08 '20 11:07 frm

Most of developers introduce external libs for reducing mental burden. Because of that, I think it would be enough to tell them "how to do that", rather than "why to do that".

Curious boys will read the code.

Create a new PR - #13 for "how to do that".

c4710n avatar Jan 20 '21 16:01 c4710n

But I'm sure that many developers would like to know the reason why a descision was made...

railsmechanic avatar Jan 20 '21 19:01 railsmechanic

This piqued my interest and I thought of :persistent_term as a possible solution. Here's the benchmark results based on 41c6285

  Operating System: Linux
  CPU Information: AMD Ryzen 9 3900X 12-Core Processor
  Number of Available Cores: 24
  Available memory: 62.79 GB
  Elixir 1.13.0
  Erlang 24.1.7

  Benchmark suite executing with the following configuration:
  warmup: 2 s
  time: 5 s
  memory time: 0 ns
  reduction time: 0 ns
  parallel: 1
  inputs: none specified
  Estimated total run time: 21 s

  Benchmarking nanoid ...
  Benchmarking nanoid_ag ...
  Benchmarking nanoid_pt ...

  Name                ips        average  deviation         median         99th %
  nanoid          18.18 K       54.99 μs    ±11.61%       54.79 μs       67.70 μs
  nanoid_pt       17.51 K       57.10 μs    ±10.40%       56.86 μs       70.22 μs
  nanoid_ag       17.15 K       58.32 μs    ±10.16%       57.97 μs       74.22 μs

  Comparison:
  nanoid          18.18 K
  nanoid_pt       17.51 K - 1.04x slower +2.10 μs
  nanoid_ag       17.15 K - 1.06x slower +3.33 μs

5% slower for both is a lot better than the 14% reported in #6.

Maybe that 5% hit is worth it to be able to change the defaults w/o recompiling? Maybe not :wink:

jc00ke avatar Mar 31 '22 01:03 jc00ke

Seems like newer Elixir version don't have that problem.

I've created a new project, installed :nanoid, and ran it:

$ iex -S mix
==> nanoid
Compiling 4 files (.ex)
Generated nanoid app
==> nanoid_example
Compiling 1 file (.ex)
Generated nanoid_example app
Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Interactive Elixir (1.15.7) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Nanoid.generate
"F0BE-ZmwBL2g_ZC7Zj-qg"
iex(2)> byte_size Nanoid.generate
21

Then changed the configuration in config/config.exs

import Config
config :nanoid, size: 10

And ran it again

$ iex -S mix
==> nanoid
Compiling 4 files (.ex)
Generated nanoid app
==> nanoid_example
Generated nanoid_example app
Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Interactive Elixir (1.15.7) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Nanoid.generate
"gA7DmeUhRz"
iex(2)> byte_size Nanoid.generate
10

Note that nanoid was recompiled and the generated values were of the requested size.

ruslandoga avatar Nov 22 '23 09:11 ruslandoga