luxon icon indicating copy to clipboard operation
luxon copied to clipboard

`toRelative` is really slow

Open andreialecu opened this issue 3 years ago • 7 comments

Describe the bug

Not necessarily a bug, but toRelative() is really slow and the reason isn't the Intl api.

I'm using luxon in a react-native app, and when formatting dates on a mid-level Android phone, a single call to .toRelative() takes around 15 milliseconds to return.

When you have a bigger list, this absolutely kills performance.

I tracked it down to this function: https://github.com/moment/luxon/blob/9a7e46b6e635db89c1c94e18a8c53c28341b7c5e/src/datetime.js#L1782-L1802

You can also verify this by running the built in benchmark in luxon at: https://github.com/moment/luxon/blob/e0c8f874304cd4ecc3944bdcff3d8f8c27102a18/benchmarks/datetime.js#L59-L61

Which can only do around 10000 operations per second on my M1 cpu in NodeJS. Others functions in that benchmark can do hundreds of thousands, so it's definitely a problem.

To Reproduce

Run the benchmark at https://github.com/moment/luxon/blob/e0c8f874304cd4ecc3944bdcff3d8f8c27102a18/benchmarks/datetime.js#L59-L61

Actual vs Expected behavior

Should not be so slow.

Desktop (please complete the following information):

  • OS: Android
  • Browser: React-Native
  • Luxon version: 1.25.0

Additional context

Note that using https://www.npmjs.com/package/javascript-time-ago is much much faster (less than 1 ms per format on the same Android device), so I'm sure some improvements are possible

andreialecu avatar Jun 17 '21 11:06 andreialecu

Here's a quick benchmark:

const Benchmark = require("benchmark");
const { DateTime } = require("luxon");

const TimeAgo = require("javascript-time-ago");
const en = require("javascript-time-ago/locale/en");

const suite = new Benchmark.Suite();

const dt = DateTime.now().minus({ days: 42 });
const date = dt.toJSDate();

TimeAgo.addLocale(en)
const ta = new TimeAgo("en");

console.log(dt.toRelative());
console.log(ta.format(date));

suite
  .add("luxon", () => {
    dt.toRelative();
  })
  .add("javascript-time-ago", () => {
    ta.format(date);
  })
  .on("cycle", (event) => {
    console.log(String(event.target));
  })
  .on("complete", function () {
    console.log("Fastest is " + this.filter("fastest").map("name"));
  })
  .run();

Results:

1 month ago
1 month ago
luxon x 12,427 ops/sec ±1.25% (89 runs sampled)
javascript-time-ago x 249,242 ops/sec ±3.58% (93 runs sampled)
Fastest is javascript-time-ago

andreialecu avatar Jun 20 '21 07:06 andreialecu

Hi @andreialecu ! Thanks for your investigation, it's helpful. But your Luxon version that you tried currently is too old.

Could you please try it again and show the results?

vladyslavNiemtsev avatar Feb 10 '22 14:02 vladyslavNiemtsev

➜ node benchmark.js
luxon x 59,360 ops/sec ±0.69% (94 runs sampled)
javascript-time-ago x 366,422 ops/sec ±0.28% (100 runs sampled)
Fastest is javascript-time-ago

Still 6x slower than javascript-time-ago with luxon 2.3.0. Seems faster than before however.

andreialecu avatar Feb 10 '22 14:02 andreialecu

Oh, thanks for this such a quick answer!

vladyslavNiemtsev avatar Feb 10 '22 14:02 vladyslavNiemtsev

@andreialecu could you please tip me with following:

As I understand toRelative() function can't display the label like "just now" (as javascript-time-ago can) for rounded seconds, or for units array that we can pass as options into toRelative() like:

[ "years", "quarters", "months", "weeks", "days", "hours", "minutes"]

https://moment.github.io/luxon/api-docs/index.html#datetimetorelative

?

vladyslavNiemtsev avatar Feb 10 '22 14:02 vladyslavNiemtsev

I ended up not using toRelative() because of the performance problems with it on React Native Android, but as far as I remember it cannot display just now, no.

andreialecu avatar Feb 10 '22 14:02 andreialecu

Okay, thanks! @andreialecu

vladyslavNiemtsev avatar Feb 10 '22 14:02 vladyslavNiemtsev