Skip to content

Conversation

@dkuku
Copy link
Contributor

@dkuku dkuku commented Dec 1, 2025

Erlang has much faster day_to_year implementation than elixir (3x faster according to benchee). The issue is that it only works for positive days.
This is a port that works for positive and negative days.

list = Enum.to_list(10_000_000..10_000_100)

Benchee.run(
  %{
    "elixir" => fn -> Enum.map(list, &Calendar.ISO.date_from_iso_days/1) end,
    "new_elixir" => fn -> Enum.map(list, &XCalendar.ISO.date_from_iso_days/1) end,
    "erlang" => fn -> Enum.map(list, &:calendar.gregorian_days_to_date/1) end
  },
  time: 5,
  memory_time: 5,
  profile_after: :fprof
)
Operating System: Linux
CPU Information: AMD Ryzen 7 7840HS with Radeon 780M Graphics
Number of Available Cores: 16
Available memory: 27.09 GB
Elixir 1.19.3
Erlang 28.1
JIT enabled: true

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

Benchmarking elixir ...
Benchmarking erlang ...
Benchmarking new_elixir ...
Calculating statistics...
Formatting results...

Name                 ips        average  deviation         median         99th %
erlang          178.43 K        5.60 μs   ±151.11%        5.31 μs        9.43 μs
new_elixir      177.48 K        5.63 μs   ±142.16%        5.38 μs        9.30 μs
elixir           57.63 K       17.35 μs    ±45.38%       17.02 μs       23.09 μs

Comparison:
erlang          178.43 K
new_elixir      177.48 K - 1.01x slower +0.0301 μs
elixir           57.63 K - 3.10x slower +11.75 μs

Memory usage statistics:

Name          Memory usage
erlang            14.20 KB
new_elixir        11.84 KB - 0.83x memory usage -2.36719 KB
elixir            11.84 KB - 0.83x memory usage -2.36719 KB

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

Profiling erlang with fprof...
Reading trace data...
..
End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                             1408       2.574       2.574
:fprof.apply_start_stop/4                                            0       2.574       0.008
anonymous fn/2 in Benchee.Profile.run/4                              1       2.566       0.003
Benchee.Benchmark.Runner.run_once/2                                  1       2.447       0.004
Benchee.Benchmark.Runner.collect_return_value/2                      1       2.438       0.005
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                      1       2.427       0.001
Enum.map/2                                                           1       2.426       0.001
Enum."-map/2-lists^map/1-1-"/2                                     102       2.425       0.304
:calendar.gregorian_days_to_date/1                                 101       2.121       0.303
:calendar.day_to_year/1                                            101       1.212       0.404
:calendar.year_day_to_date/2                                       101       0.606       0.303
:calendar.dty/5                                                    101       0.606       0.303
:calendar.is_leap_year/1                                           202       0.404       0.202
:calendar.dy/1                                                     303       0.303       0.303
:calendar.is_leap_year1/1                                          202       0.202       0.202
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.116       0.004
:calendar.year_day_to_date2/2                                      101       0.101       0.101
Kernel.struct!/2                                                     1       0.093       0.003
Benchee.Benchmark.BenchmarkConfig.__struct__/1                       1       0.089       0.001
Enum.reduce/3                                                        1       0.088       0.001
:maps.fold/3                                                         1       0.087       0.003
:maps.fold_1/4                                                      14       0.079       0.040
anonymous fn/4 in Enum.reduce/3                                     13       0.026       0.013
Map.take/2                                                           1       0.019       0.001
Map.take/3                                                          14       0.018       0.015
:maps.try_next/2                                                    13       0.013       0.013
anonymous fn/2 in Benchee.Benchmark.BenchmarkConfig.__struct        13       0.013       0.013
Benchee.Benchmark.Hooks.run_before_scenario/2                        1       0.004       0.002
Benchee.Benchmark.Hooks.run_before_function/2                        4       0.004       0.004
Benchee.Benchmark.Hooks.run_before_each/2                            1       0.004       0.002
:maps.next/1                                                         1       0.003       0.002
:maps.from_list/1                                                    1       0.003       0.003
:maps.iterator/1                                                     1       0.002       0.001
:maps.iterator/2                                                     1       0.001       0.001
:erts_internal.map_next/3                                            1       0.001       0.001
Kernel.validate_struct!/3                                            1       0.001       0.001
Benchee.Benchmark.Runner.main_function/2                             1       0.001       0.001
Benchee.Benchmark.Hooks.run_after_scenario/2                         1       0.001       0.001
Benchee.Benchmark.Hooks.run_after_each/3                             1       0.001       0.001
:undefined                                                           0       0.000       0.000
:suspend                                                             1       0.000       0.000

Profiling new_elixir with fprof...
Reading trace data...
..
End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                             1409       2.775       2.774
:fprof.apply_start_stop/4                                            0       2.775       0.008
anonymous fn/2 in Benchee.Profile.run/4                              1       2.767       0.003
Benchee.Benchmark.Runner.run_once/2                                  1       2.651       0.004
Benchee.Benchmark.Runner.collect_return_value/2                      1       2.642       0.005
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                      1       2.631       0.001
Enum.map/2                                                           1       2.630       0.001
Enum."-map/2-lists^map/1-1-"/2                                     102       2.629       0.305
XCalendar.ISO.date_from_iso_days/1                                 101       2.323       0.404
XCalendar.ISO.days_to_year/1                                       101       1.717       0.606
XCalendar.ISO.days_to_year_interpolated/5                          101       0.707       0.404
XCalendar.ISO.floor_div_positive_divisor/2                         303       0.303       0.303
XCalendar.ISO.days_in_previous_years/1                             303       0.303       0.303
XCalendar.ISO.leap_year?/1                                         202       0.202       0.202
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.113       0.003
XCalendar.ISO.year_day_to_year_date/1                              101       0.101       0.101
Kernel.struct!/2                                                     1       0.093       0.002
Benchee.Benchmark.BenchmarkConfig.__struct__/1                       1       0.090       0.001
Enum.reduce/3                                                        1       0.089       0.001
:maps.fold/3                                                         1       0.088       0.004
:maps.fold_1/4                                                      14       0.079       0.040
anonymous fn/4 in Enum.reduce/3                                     13       0.026       0.013
Map.take/2                                                           1       0.017       0.001
Map.take/3                                                          14       0.016       0.015
:maps.try_next/2                                                    13       0.013       0.013
anonymous fn/2 in Benchee.Benchmark.BenchmarkConfig.__struct        13       0.013       0.013
Benchee.Benchmark.Hooks.run_before_scenario/2                        1       0.004       0.002
Benchee.Benchmark.Hooks.run_before_function/2                        4       0.004       0.004
Benchee.Benchmark.Hooks.run_before_each/2                            1       0.004       0.002
:maps.next/1                                                         1       0.003       0.002
:maps.iterator/1                                                     1       0.002       0.001
:maps.iterator/2                                                     1       0.001       0.001
:maps.from_list/1                                                    1       0.001       0.001
:erts_internal.map_next/3                                            1       0.001       0.001
Kernel.validate_struct!/3                                            1       0.001       0.001
Benchee.Benchmark.Runner.main_function/2                             1       0.001       0.001
Benchee.Benchmark.Hooks.run_after_scenario/2                         1       0.001       0.001
Benchee.Benchmark.Hooks.run_after_each/3                             1       0.001       0.001
:suspend                                                             2       0.001       0.000
:undefined                                                           0       0.000       0.000

Profiling elixir with fprof...
Reading trace data...
.......
End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                             4440       7.025       7.023
:fprof.apply_start_stop/4                                            0       7.025       0.008
anonymous fn/2 in Benchee.Profile.run/4                              1       7.017       0.003
Benchee.Benchmark.Runner.run_once/2                                  1       6.895       0.004
Benchee.Benchmark.Runner.collect_return_value/2                      1       6.886       0.005
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                      1       6.875       0.001
Enum.map/2                                                           1       6.874       0.001
Enum."-map/2-lists^map/1-1-"/2                                     102       6.873       0.304
Calendar.ISO.date_from_iso_days/1                                  101       6.569       0.404
Calendar.ISO.days_to_year/1                                        101       5.963       0.303
Calendar.ISO.days_to_year/3                                       1919       5.559       3.739
Calendar.ISO.days_in_previous_years/1                             1919       1.919       1.919
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.119       0.003
Calendar.ISO.year_day_to_year_date/2                               101       0.101       0.101
Calendar.ISO.leap_year?/1                                          101       0.101       0.101
Kernel.struct!/2                                                     1       0.097       0.003
Benchee.Benchmark.BenchmarkConfig.__struct__/1                       1       0.093       0.001
Enum.reduce/3                                                        1       0.092       0.001
:maps.fold/3                                                         1       0.091       0.004
:maps.fold_1/4                                                      14       0.082       0.040
anonymous fn/4 in Enum.reduce/3                                     13       0.029       0.013
Map.take/2                                                           1       0.019       0.002
Map.take/3                                                          14       0.017       0.016
anonymous fn/2 in Benchee.Benchmark.BenchmarkConfig.__struct        13       0.016       0.016
:maps.try_next/2                                                    13       0.013       0.013
Benchee.Benchmark.Hooks.run_before_scenario/2                        1       0.004       0.002
Benchee.Benchmark.Hooks.run_before_function/2                        4       0.004       0.004
Benchee.Benchmark.Hooks.run_before_each/2                            1       0.004       0.002
:maps.next/1                                                         1       0.003       0.002
:maps.iterator/1                                                     1       0.002       0.001
:suspend                                                             3       0.002       0.000
:maps.iterator/2                                                     1       0.001       0.001
:maps.from_list/1                                                    1       0.001       0.001
:erts_internal.map_next/3                                            1       0.001       0.001
Kernel.validate_struct!/3                                            1       0.001       0.001
Benchee.Benchmark.Runner.main_function/2                             1       0.001       0.001
Benchee.Benchmark.Hooks.run_after_scenario/2                         1       0.001       0.001
Benchee.Benchmark.Hooks.run_after_each/3                             1       0.001       0.001
:undefined                                                           0       0.000       0.000

@josevalim josevalim merged commit 6b519ae into elixir-lang:main Dec 1, 2025
12 checks passed
@josevalim
Copy link
Member

💚 💙 💜 💛 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants