Skip to content

Factor calendar denominators before multiplying - #15879

Open
preciz wants to merge 1 commit into
elixir-lang:mainfrom
preciz:optimize-calendar-denominators
Open

Factor calendar denominators before multiplying#15879
preciz wants to merge 1 commit into
elixir-lang:mainfrom
preciz:optimize-calendar-denominators

Conversation

@preciz

@preciz preciz commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Assisted-by: Codex CLI:GPT 6
Assisted-by: Antigravity:Gemini-3.8-Flash

Reduce intermediate integer sizes by dividing out common denominator factors before multiplication. This speeds up mixed-denominator day-fraction addition while preserving exact results.

Results:

   Microsecond + Nanosecond        |     7.02 M ips (142.5 ns)      |     12.25 M ips (81.6 ns)      |    1.75x faster (-60.9 ns)
   Custom mixed denominators       |     7.33 M ips (136.5 ns)      |     12.21 M ips (81.9 ns)      |    1.67x faster (-54.6 ns)
   Coprime / unaligned             |     9.42 M ips (106.2 ns)      |     11.02 M ips (90.8 ns)      |    1.17x faster (-15.4 ns)

Bench:

Mix.install([:benchee])

defmodule Before do
  def add_day_fraction_to_iso_days({days, {parts, ppd}}, add, add_ppd) do
    parts = parts * add_ppd
    add = add * ppd
    gcd = Integer.gcd(ppd, add_ppd)
    result_parts = div(parts + add, gcd)
    result_ppd = div(ppd * add_ppd, gcd)
    normalize_iso_days(days, result_parts, result_ppd)
  end

  defp normalize_iso_days(days, parts, ppd) do
    days_offset = div(parts, ppd)
    parts = rem(parts, ppd)
    if parts < 0, do: {days + days_offset - 1, {parts + ppd, ppd}}, else: {days + days_offset, {parts, ppd}}
  end
end

defmodule After do
  def add_day_fraction_to_iso_days({days, {parts, ppd}}, add, add_ppd) do
    gcd = Integer.gcd(ppd, add_ppd)
    ppd_factor = div(ppd, gcd)
    add_ppd_factor = div(add_ppd, gcd)
    result_parts = parts * add_ppd_factor + add * ppd_factor
    result_ppd = ppd * add_ppd_factor
    normalize_iso_days(days, result_parts, result_ppd)
  end

  defp normalize_iso_days(days, parts, ppd) do
    days_offset = div(parts, ppd)
    parts = rem(parts, ppd)
    if parts < 0, do: {days + days_offset - 1, {parts + ppd, ppd}}, else: {days + days_offset, {parts, ppd}}
  end
end

inputs = %{
  "microsecond + nanosecond (typical DateTime.add with nanoseconds)" => {
    {730_000, {43_200_000_000, 86_400_000_000}},
    500_000,
    86_400_000_000_000
  },
  "custom mixed denominators (large ppd)" => {
    {730_000, {123_456_789, 1_000_000_000}},
    987_654_321,
    2_000_000_000
  },
  "arbitrary coprime / unaligned denominators" => {
    {730_000, {500_000, 1_000_000}},
    300_000,
    700_000
  }
}

Benchee.run(
  %{
    "before" => fn {iso_days, add, add_ppd} -> Before.add_day_fraction_to_iso_days(iso_days, add, add_ppd) end,
    "after" => fn {iso_days, add, add_ppd} -> After.add_day_fraction_to_iso_days(iso_days, add, add_ppd) end
  },
  inputs: inputs,
  time: 2,
  memory_time: 2
)

Reduce intermediate integer sizes by dividing out common denominator factors before multiplication. This speeds up mixed-denominator day-fraction addition while preserving exact results.
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.

1 participant