Skip to content

localeCompare extremely slow sometimes #867

@gnprice

Description

@gnprice

Bug Description

  • I have run gradle clean and confirmed this bug does not occur with JSC

Hermes version: (whatever's in RN 0.70.6)
React Native version (if any): 0.70.6 (the current latest)
OS version (if any): Android 13
Platform (most likely one of arm64-v8a, armeabi-v7a, x86, x86_64): arm64-v8a

Steps To Reproduce

  1. Clone https://github.com/gnprice/hermes-localecompare-repro .
  2. Run it with npx react-native run-android.
  3. Observe the log lines showing calls to String.prototype.localeCompare taking multiple seconds each. For example:
 LOG  sorting...
 LOG  slow localeCompare: 8836ms for 'vihmdxim' vs 'vlqkdtzn'
 LOG  slow localeCompare: 2078ms for 'ezkvpvbg' vs 'ivkpfinh'
 LOG  slow localeCompare: 6568ms for 'ctanfzkj' vs 'ctjhzdyi'
 LOG  slow localeCompare: 7639ms for 'hgyzpilj' vs 'hhpnlwqf'
 LOG  slow localeCompare: 7337ms for 'jzninmrt' vs 'orbuzdcq'
 LOG  slow localeCompare: 6872ms for 'rnarhxnr' vs 'rnauqlwl'
 LOG  slow localeCompare: 3648ms for 'nfdeptde' vs 'nfgpjekx'
 LOG  ...done, took 56938ms

Those extremely slow calls are rare relative to the total number of calls. But because they're so extremely slow, they account for most of the total time — about 75% of the whole sort in this example.

code example

The repro repo above just consists of a fresh React Native app on the latest version (from npx react-native init), with the following code added at the top of the render function:

  const randInt = (end, start = 0) =>
    Math.floor(Math.random() * (end - start) + start);
  const alphabet = 'abcdefghijklmnopqrstuvwxyz';
  const names = Array.from({length: 30000}, () =>
    Array.from({length: 8}, () => alphabet[randInt(alphabet.length)]).join(''),
  );

  console.log('sorting...');
  const s = Date.now();
  names.sort(localeCompare);
  console.log(`...done, took ${Date.now() - s}ms`);

  function localeCompare(a, b) {
    const s = Date.now();
    const result = a.localeCompare(b);
    const e = Date.now();
    if (e - s > 100) {
      console.log(`slow localeCompare: ${e - s}ms for '${a}' vs '${b}'`);
    }
    return result;
  }

That is, it makes up a long list of boring short alphabetic strings, and sorts them. The comparator just calls String.prototype.localeCompare, and times the call.

The Expected Behavior

Calling localeCompare on a string should be fast — certainly far less than a second, for any non-gigantic strings.

Other notes

I do not reproduce the issue when running in an emulated Android device, on x86_64. So it may be architecture-dependent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions