Profiling BookNest

Systrace and Android Profiling found that sorting 5,000 titles with localeCompare() took seconds. The DevTools Performance panel records a CPU profile; scripts/cdp.mjs records the same Hermes 11,328 sampling profile over CDP and adds up the samples by function, here while the demo's three sort buttons were tapped:

Recording a 30-second CPU profile of the running appShell
node scripts/cdp.mjs profile 30
Output
profiling for 30 s
 19147 ms  [root]  [root]
  9820 ms  [Native] stringPrototypeLocaleCompare  [native]
   381 ms  [Native] arrayPrototypeSort  [native]
   317 ms  [Native] intlCollatorCompare  [native]
   222 ms  (anonymous)  app bundle
...

[root] is idle time. Nearly all the rest is localeCompare itself, inside Hermes, not in BookNest's code. On Android, Hermes implements its Intl features with the platform's Java APIs, so each comparison crosses JNI and sets up collation again, and the Hermes issue tracker has reports of single calls taking seconds (facebook/hermes#867 (https://github.com/facebook/hermes/issues/867 11,328 )). The demo times three versions:

src/demos/Perf.tsx: three ways to sort 5,000 titles (excerpt)TSX
  ['localeCompare', (a) => a.sort((x, y) => x.title.localeCompare(y.title))],
  ['Intl.Collator', (a) => { const c = new Intl.Collator('en').compare;
    return a.sort((x, y) => c(x.title, y.title)); }],
  ['precomputed keys', (a) => a.map((b) => [b.title.toLowerCase(), b] as const)
    .sort(([x], [y]) => (x < y ? -1 : x > y ? 1 : 0)).map(([, b]) => b)],
Output
 LOG  localeCompare: 11015 ms
 LOG  Intl.Collator: 552 ms
 LOG  precomputed keys: 102 ms

One Intl.Collator, created once and reused, was 20 times faster; precomputed lowercase keys compared with < were 100 times faster, but they sort by code point, so accented titles land out of dictionary order. Use the keys for BookNest's English catalog, the collator for other languages, or sort on the server.