- Dharmagya
- panchang-ts docs
- Performance
Performance
panchang-ts · v5.0.0 · MIT
Measured costs, the sections and computeEndTimes levers, process-wide caches, and the React Native / Hermes rendering pattern.
Measured at Pune on an Apple M-series laptop under Node, median of 11 processes per configuration. Treat them as relative guidance, not a spec — they move with hardware, latitude and date. Distinct days is the calendar-scan cost (every call misses the solar rise/set cache); same day repeated is what a UI that re-renders one date sees. The last column is the published 4.3.1 package, installed from npm and benchmarked beside v5.
getDailyPanchang | Distinct days | Same day repeated | 4.3.1 (distinct) |
|---|---|---|---|
| Default (all sections + end-times) | ~0.41 ms | ~0.17 ms | ~6.06 ms |
computeEndTimes: false | ~0.39 ms | ~0.15 ms | ~5.63 ms |
Without 'festivals' | ~0.39 ms | — | n/a |
sections: ['festivals', 'eclipse'] | ~0.40 ms | — | n/a |
sections: [] | ~0.27 ms | — | n/a |
sections: [] + computeEndTimes: false | ~0.25 ms | ~0.14 ms | n/a |
getInstantPanchang | ~0.21 ms | ~0.10 ms | ~0.43 ms |
A default day is ~15× cheaper than in 4.3.1, and a repeated day ~37×. Most of that is not tuning: 4.3.1 ran a full lunar theory inside the eclipse search on every day containing a syzygy, and had no cache that survived a call.
The two levers
Cost is dominated by ephemeris evaluations, so the lever that matters is the one that avoids them:
| Lever | What it does | Worth |
|---|---|---|
sections | skip the optional ephemeris-backed blocks you do not need | ~0.41 → ~0.27 ms when everything optional is off |
computeEndTimes: false | skip transition searches — drops endTime fields, saves arithmetic, not ephemeris | ~5% cold, ~10% warm — use it to drop fields you do not want, not to go faster |
Narrowing the work
PanchangSection lists the four optional blocks. Everything else a daily panchang returns — the five elements, slot systems, muhurtas, inauspicious periods, masa / samvat / rashi — is arithmetic over the sunrise / sunset / next-sunrise triplet and is always computed, because skipping it would save nothing.
| Section | Covers | Fields when omitted |
|---|---|---|
'festivals' | festival detection — needs the prior day, canonical-time anchors, next-day transit | festivals: [] — but an eclipse entry is still prepended when 'eclipse' is on |
'eclipse' | eclipse overlapping the Hindu day | eclipse: null |
'moonTimes' | moon.rise / moon.set | null |
'lunarWindows' | Bhadra, Varjyam, Panchaka-Rahita — each binary-searches lunar longitude across the day | null / [] |
// Everything (default).
getDailyPanchang(date, loc, { timezone: 330 });
// Festivals only — no moon times, no Bhadra/Varjyam windows.
getDailyPanchang(date, loc, {
timezone: 330,
sections: ['festivals', 'eclipse'],
});
// Cheapest useful call: elements, slots, muhurtas and inauspicious periods
// only. Those are arithmetic on the sunrise triplet and are always computed.
getDailyPanchang(date, loc, {
timezone: 330,
sections: [],
computeEndTimes: false,
});Caches and the range helpers
Repeated calls for the same location-day are cheaper because solar rise/set events are cached process-wide, keyed on (direction, lat, lon, elevation, UTC day) and bounded at 20,000 entries. The cache also makes sunrise single-valued — two calls for the same day agree exactly.
Range helpers apply the same narrowing internally: computeEkadashiDatesForYear reads only the tithi at sunrise (~18 ms for a full year, against ~2,360 ms in 4.3.1); computeFestivalsInRange keeps only 'festivals' and 'eclipse' (~131 ms/year, against ~2,180); computeSankrantisForYear scans one solar longitude per day and bisects the 12 transits (~3.3 ms/year, against ~154).
Where v5 is slower — and why
The own-ephemeris series keep roughly three times the old accuracy against JPL DE441, and evaluation is sine-bound, so more terms cost proportionally more:
| Surface | v5 vs 4.3.1 | Context |
|---|---|---|
getSiderealSunLongitude / getSiderealMoonLongitude | ~2× per raw call | every documented workflow amortizes these through caches and is faster overall; visible only in tight loops over distinct instants — prefer the range APIs there |
computeRashiChart / computeNavamsa | ~0.32 ms vs ~0.10 ms | a chart is fifteen full-accuracy planet evaluations; the price of Mercury 6.50″ → 0.30″ against DE441 |
computeShadbala / computeBhavaBala | ~2× faster | v5 computes the positions once and reuses them (~0.33 ms vs ~0.72) |
React Native / Hermes
Works with Expo and bare React Native (Hermes engine). Pass timezone as a number — IANA strings need Intl, which older Hermes versions lack. Two-pass rendering pattern for a smooth UI:
import { getDailyPanchang } from 'panchang-ts';
import { InteractionManager } from 'react-native';
// Pass 1 — cheapest useful result: elements, slots, muhurtas.
const fast = getDailyPanchang(date, location, {
timezone: 330,
sections: [],
computeEndTimes: false,
});
setState(fast);
// Pass 2 — background, everything.
InteractionManager.runAfterInteractions(() => {
setState(getDailyPanchang(date, location, { timezone: 330 }));
});The published numbers above are Node measurements; Hermes figures will be published once measured on device. What carries over is the shape of the cost — it is dominated by ephemeris math, so the sections and computeEndTimes levers have the same proportional effect on any runtime. Built tables are dictionary-encoded specifically because parse time and resident memory are the constraints that bite on Hermes — see the table pattern.
