Skip to content

Conversation

@dkuku
Copy link
Contributor

@dkuku dkuku commented Dec 6, 2025

This is a faster implementation than the erlang one. The reference implementation is mit licensed I adjusted it for elixir needs using windsurf with claude opus.
2x speedup comparing to the version from last week.

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: 24 s
Excluding outliers: false

Benchmarking elixir ...
Benchmarking neri-scheneider ...
Calculating statistics...
Formatting results...

Name                      ips        average  deviation         median         99th %
neri-scheneider      400.85 K        2.49 μs   ±281.71%        2.32 μs        3.41 μs
elixir               174.93 K        5.72 μs   ±147.04%        5.50 μs        9.25 μs

Comparison:
neri-scheneider      400.85 K
elixir               174.93 K - 2.29x slower +3.22 μs

Memory usage statistics:

Name               Memory usage
neri-scheneider         4.73 KB
elixir                 11.84 KB - 2.50x memory usage +7.10 KB

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

Profiling neri-scheneider with fprof...
Reading trace data...

End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                              297       0.565       0.565
:fprof.apply_start_stop/4                                            0       0.565       0.009
anonymous fn/2 in Benchee.Profile.run/4                              1       0.556       0.004
Benchee.Benchmark.Runner.run_once/2                                  1       0.427       0.004
Benchee.Benchmark.Runner.collect_return_value/2                      1       0.418       0.005
anonymous fn/1 in :elixir_compiler_59.__FILE__/1                     1       0.407       0.001
Enum.map/2                                                           1       0.406       0.001
Enum."-map/2-lists^map/1-1-"/2                                     102       0.405       0.304
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.125       0.003
XCalendar.ISO.date_from_iso_days/1                                 101       0.101       0.101
Kernel.struct!/2                                                     1       0.097       0.004
Benchee.Benchmark.BenchmarkConfig.__struct__/1                       1       0.092       0.001
Enum.reduce/3                                                        1       0.091       0.002
:maps.fold/3                                                         1       0.089       0.004
:maps.fold_1/4                                                      14       0.080       0.040
anonymous fn/4 in Enum.reduce/3                                     13       0.027       0.014
Map.take/2                                                           1       0.025       0.002
Map.take/3                                                          14       0.023       0.020
: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 elixir with fprof...
Reading trace data...
..
End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                             1409       2.778       2.777
:fprof.apply_start_stop/4                                            0       2.778       0.007
anonymous fn/2 in Benchee.Profile.run/4                              1       2.771       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_59.__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
Calendar.ISO.date_from_iso_days/1                                  101       2.323       0.404
Calendar.ISO.days_to_year/1                                        101       1.717       0.606
Calendar.ISO.days_to_year_interpolated/5                           101       0.707       0.404
Calendar.ISO.floor_div_positive_divisor/2                          303       0.303       0.303
Calendar.ISO.days_in_previous_years/1                              303       0.303       0.303
Calendar.ISO.leap_year?/1                                          202       0.202       0.202
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.117       0.003
Calendar.ISO.year_day_to_year_date/1                               101       0.101       0.101
Kernel.struct!/2                                                     1       0.095       0.004
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.019       0.001
Map.take/3                                                          14       0.018       0.016
: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.from_list/1                                                    1       0.002       0.002
: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
:suspend                                                             2       0.001       0.000
:undefined                                                           0       0.000       0.000


days_in_previous_years(year) + days_before_month(month) + leap_day_offset(year, month) + day -
1
y = if month <= 2, do: year - 1, else: year
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could mention the name of the algorithm in a comment with a link for future reference?

@leonqadirie
Copy link

leonqadirie commented Dec 7, 2025

Was very intrigued, took a look and stumbled across this recent article claiming to surpass Neri-Schneider.

A New Faster Algorithm for Gregorian Date Conversion - also note the mention of an even faster version by now.

It might be of interest, if not, apologies for the noise :)

@dkuku
Copy link
Contributor Author

dkuku commented Dec 7, 2025

Was very intrigued, took a look and stumbled across this recent article claiming to surpass Neri-Schneider.

A New Faster Algorithm for Gregorian Date Conversion - also note the mention of an even faster version by now.

It might be of interest, if not, apologies for the noise :)

Same here: this new algorithm made me curious how we do it. :) The mentioned algorithm from last week is implemented in c and uses compiler tricks that can't be implemented on the beam: like bit shifting a 128bit number by 64bits that is 'free' on modern computers.
There is also an issue with it: it counts backwards from some very big year that you pick, so it is not infinite.
Second problem was that before year 0 I saw off by one errors. Maybe I was doing something wrong, but because the first limitation I decided to leave the idea and pick something else.

@dkuku
Copy link
Contributor Author

dkuku commented Dec 7, 2025

here a benchmark for the function date_to_iso_days:

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: 24 s
Excluding outliers: false

Benchmarking elixir ...
Benchmarking neri-scheneider ...
Calculating statistics...
Formatting results...

Name                      ips        average  deviation         median         99th %
neri-scheneider      511.46 K        1.96 μs   ±344.78%        1.85 μs        2.63 μs
elixir               467.63 K        2.14 μs   ±321.91%        2.06 μs        3.25 μs

Comparison:
neri-scheneider      511.46 K
elixir               467.63 K - 1.09x slower +0.183 μs

Memory usage statistics:

Name               Memory usage
neri-scheneider         1.58 KB
elixir                  1.58 KB - 1.00x memory usage +0 KB

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

Profiling neri-scheneider with fprof...
Reading trace data...
.
End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                              802       1.366       1.366
:fprof.apply_start_stop/4                                            0       1.366       0.009
anonymous fn/2 in Benchee.Profile.run/4                              1       1.357       0.004
Benchee.Benchmark.Runner.run_once/2                                  1       1.235       0.004
Benchee.Benchmark.Runner.collect_return_value/2                      1       1.226       0.005
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                      1       1.215       0.001
Enum.map/2                                                           1       1.214       0.001
Enum."-map/2-lists^map/1-1-"/2                                     102       1.213       0.304
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                    101       0.909       0.101
XCalendar.ISO.date_to_iso_days/3                                   101       0.808       0.202
XCalendar.ISO.ensure_day_in_month!/3                               101       0.606       0.202
XCalendar.ISO.days_in_month/2                                      101       0.404       0.101
XCalendar.ISO.days_in_month_guarded/2                              101       0.303       0.202
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.118       0.003
XCalendar.ISO.leap_year?/1                                         101       0.101       0.101
Kernel.struct!/2                                                     1       0.095       0.003
Benchee.Benchmark.BenchmarkConfig.__struct__/1                       1       0.091       0.002
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.020       0.001
Map.take/3                                                          14       0.019       0.017
: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.from_list/1                                                    1       0.002       0.002
: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 elixir with fprof...
Reading trace data...
.
End of trace!
Processing data...
Creating output...
Done!

                                                                   CNT    ACC (ms)    OWN (ms)
Total                                                             1105       1.969       1.969
:fprof.apply_start_stop/4                                            0       1.969       0.009
anonymous fn/2 in Benchee.Profile.run/4                              1       1.960       0.003
Benchee.Benchmark.Runner.run_once/2                                  1       1.841       0.004
Benchee.Benchmark.Runner.collect_return_value/2                      1       1.832       0.005
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                      1       1.821       0.001
Enum.map/2                                                           1       1.820       0.001
Enum."-map/2-lists^map/1-1-"/2                                     102       1.819       0.304
anonymous fn/1 in :elixir_compiler_2.__FILE__/1                    101       1.515       0.101
Calendar.ISO.date_to_iso_days/3                                    101       1.414       0.505
Calendar.ISO.ensure_day_in_month!/3                                101       0.606       0.202
Calendar.ISO.days_in_month/2                                       101       0.404       0.101
Calendar.ISO.days_in_month_guarded/2                               101       0.303       0.202
Benchee.Benchmark.BenchmarkConfig.from/1                             1       0.116       0.003
Calendar.ISO.leap_year?/1                                          101       0.101       0.101
Calendar.ISO.leap_day_offset/2                                     101       0.101       0.101
Calendar.ISO.days_in_previous_years/1                              101       0.101       0.101
Calendar.ISO.days_before_month/1                                   101       0.101       0.101
Kernel.struct!/2                                                     1       0.094       0.003
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.019       0.001
Map.take/3                                                          14       0.018       0.016
: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.from_list/1                                                    1       0.002       0.002
: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

@josevalim josevalim merged commit 904adda into elixir-lang:main Dec 7, 2025
11 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.

4 participants