Skip to content

Reducing memory footprint by reusing Intl.DateTimeFormat instances #7

Closed
@ferk6a

Description

@ferk6a

Hello. I was going to post this on the original proposal-temporal repo, but since the goal there wasn't about making a polyfill for production use, I decided not to. However, since this polyfill has that goal, it seems like a relevant issue.

When using the polyfill, I ran into a deno memory issue (which I reported in denoland/deno#10721). The root problem is that in the polyfill, there are a lot of new Intl.DateTimeFormat calls, objects which V8 apparently doesn't clean up as well as it could. After some investigation, I found out node is affected too, albeit some orders of magnitude less.

The issue can be demonstrated in Temporal as follows:

import { Temporal } from '@js-temporal/polyfill';
const { ZonedDateTime } = Temporal;

setInterval(() => {
  var a;
  for (let i = 0; i < 100; i++) {
    a = ZonedDateTime.from("2021-01-01T00:00:00-03:00[America/Sao_Paulo]");
    a = Temporal.now.zonedDateTimeISO().toString();
  }
}, 1);

Which ranks at ~567M resident memory in node, and ~1033M in (newer) deno.

My solution to this problem originally, was to fork the repo for my personal use and implement a cache for these objects. A Map caches IntlDateTimeFormats at every site they are required, as such:

// ecmascript.mjs
const IntlDateTimeFormatEnUsCache = new Map();

// ...
  GetCanonicalTimeZoneIdentifier: (timeZoneIdentifier) => {
    // ...
    if (!IntlDateTimeFormatEnUsCache.has(String(timeZoneIdentifier))) {
      IntlDateTimeFormatEnUsCache.set(String(timeZoneIdentifier), new IntlDateTimeFormat('en-us', {
        timeZone: String(timeZoneIdentifier), hour12: false, era: 'short',
        year: 'numeric', month: 'numeric', day: 'numeric',
        hour: 'numeric', minute: 'numeric', second: 'numeric'
      }));
    }
    const formatter = IntlDateTimeFormatEnUsCache.get(String(timeZoneIdentifier));

Node's memory footprint in that same code above went to ~100K after this change. Deno scored at ~239M (which is about the floor memory usage of deno currently).

This is an issue that V8 could fix, but it can also be done at the library level.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions