nusmodifications / nusmods

🏫 Official course planning platform for National University of Singapore.
https://nusmods.com
MIT License
557 stars 270 forks source link

perf: MPE mod lookup with map instead of linear search #3685

Closed davidlhw closed 3 months ago

davidlhw commented 3 months ago

Context

closes #3674

Implementation

Profiling

tl;dr - using Map to lookup modulecodes is about 50x faster at n=1000 than .find(...)

step 1: generate a large array of random moduleCodes and create a mock fetchedSubmission object and fetchedMpeModuleList object

const n = 10000;

const moduleCodes = Array.from({ length: n }, () =>
  (Math.random() + 1).toString(36).substring(2)
);

const fetchedMpeModuleList = moduleCodes.map((code) => ({ moduleCode: code }));
const fetchedSubmission = {
  preferences: fetchedMpeModuleList.slice(Math.floor(n / 2)),  // half of fetchedMpeModuleList
};

step 2: profile original implementation

console.time("original");
const out1 = fetchedSubmission.preferences.map((preference) => {
  const mpeModule = fetchedMpeModuleList.find(
    (m) => m.moduleCode === preference.moduleCode
  );
  if (!mpeModule) return preference;
  return {
    ...preference,
    test: true,
  };
});
console.timeEnd("original");  // original: 557.182ms
console.log(out1.length);  // 5000

step 3a: profile Map implementation

console.time("map");
const modMap = new Map(
  fetchedMpeModuleList.map((module) => [module.moduleCode, module])
);
const out2 = fetchedSubmission.preferences.map((preference) => {
  if (!modMap.get(preference.moduleCode)) return preference;
  return {
    ...preference,
    test: true,
  };
});
console.timeEnd("map");  // map: 8.299ms
console.log(out2.length);  // 5000

step 3b: profile object implementation

console.time("obj");
const modObj = fetchedMpeModuleList.reduce((lookup, module) => {
  lookup[module.moduleCode] = module;
  return lookup;
}, {});
const out3 = fetchedSubmission.preferences.map((preference) => {
  if (!modObj[preference.moduleCode]) return preference;
  return {
    ...preference,
    test: true,
  };
});
console.timeEnd("obj");  // obj: 8.366ms
console.log(out3.length);  // 5000

Other Information

vercel[bot] commented 3 months ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nusmods-export ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 26, 2024 2:34am
nusmods-website ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 26, 2024 2:34am
vercel[bot] commented 3 months ago

@davidlhw is attempting to deploy a commit to a Personal Account owned by @nusmodifications on Vercel.

@nusmodifications first needs to authorize it.

codecov[bot] commented 3 months ago

Codecov Report

Attention: Patch coverage is 0% with 3 lines in your changes are missing coverage. Please review.

Project coverage is 53.58%. Comparing base (b20b4ce) to head (c888c07).

Files Patch % Lines
website/src/views/mpe/form/MpeFormContainer.tsx 0.00% 3 Missing :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #3685 +/- ## ========================================== - Coverage 53.61% 53.58% -0.03% ========================================== Files 272 272 Lines 5976 5977 +1 Branches 1428 1428 ========================================== - Hits 3204 3203 -1 - Misses 2772 2774 +2 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

alissayarmantho commented 3 months ago

LGTM, nice work at using map instead of find 👍