Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf functions getEntriesByName and getEntriesByType seem to have unnecessarily bad performance #42004

Closed
tomjw64 opened this issue Feb 16, 2022 · 8 comments · Fixed by #42032
Labels
perf_hooks Issues and PRs related to the implementation of the Performance Timing API. performance Issues and PRs related to the performance of Node.js.

Comments

@tomjw64
Copy link

tomjw64 commented Feb 16, 2022

Version

v16.14.0

Platform

Linux 5.13.0-28-generic #31~20.04.1-Ubuntu SMP Wed Jan 19 14:08:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

internal/lib/perf

What steps will reproduce the bug?

// slow.js
let iter = 0

const getEntries = process.argv[2] === 'name'
  ? () => performance.getEntriesByName('x')
  : () => performance.getEntriesByType('measure')

while (++iter <= process.argv[3]) {
  performance.mark('x')
  performance.mark('y')
  performance.measure('x', 'x', 'y')
  getEntries()
}
console.log(getEntries().length)
// fast.js
let iter = 0
let entries = {
  mark: {},
  measure: {}
}

const getEntriesByType = (type) => [].concat(...Object.values(entries[type]))
const getEntriesByName = (name) => entries.mark[name].concat(entries.measure[name])
const mark = (name) => {
  if (!entries.mark[name]) { entries.mark[name] = [] }
  entries.mark[name].push(performance.mark(name))
}
const measure = (name, start, end) => {
  if (!entries.measure[name]) { entries.measure[name] = [] }
  entries.measure[name].push(performance.measure(name, start, end))
}

const getEntries = process.argv[2] === 'name'
  ? () => getEntriesByName('x')
  : () => getEntriesByType('measure')

while (++iter <= process.argv[3]) {
  mark('x')
  mark('y')
  measure('x', 'x', 'y')
  getEntries()
}
console.log(getEntries().length)
$ time node slow.js name 10000
20000

real	0m15.021s
user	0m14.930s
sys	0m0.196s
$ time node slow.js type 10000
10000

real	0m7.476s
user	0m7.482s
sys	0m0.020s
$ time node fast.js name 10000
20000

real	0m0.188s
user	0m0.117s
sys	0m0.117s
$ time node fast.js type 10000
10000

real	0m0.083s
user	0m0.107s
sys	0m0.009s

How often does it reproduce? Is there a required condition?

Lots of marks or measures must be present to see a noticeable difference in performance. Additionally, the name parameter for the getEntriesByName function must be for a entry name that exists in the buffers, as far as I can tell.

What is the expected behavior?

No response

What do you see instead?

getEntriesByName and getEntriesByType take significantly longer than a naive entry tracking solution using object key lookups and concatenation.

Additional information

kMaxPerformanceEntryBuffers makes clear that no more than 1000000 entries should be present. Maybe that implies that the performance expectations for these functions should not be high and that users should be expected to regularly clear entries instead.

@benjamingr
Copy link
Member

benjamingr commented Feb 16, 2022

cc @legendecas @jasnell

I'm wondering why this uses linked lists and not arrays (which are faster in pretty much every way?)

@legendecas
Copy link
Member

@benjamingr it's used to prevent allocation and expansion of the array on the entry creation time, at which it is much more performance-sensitive than observing the entries.

@benjamingr
Copy link
Member

@legendecas but array allocation/size increase is amortized O(1) isn't it? If allocation is a problem we can use a FixedQueue like other places in the code probably?

@legendecas
Copy link
Member

From what I understand, expansion of array is not constant time. The content of the array has to be copied to the new larger location. I think FixedQueue could be a good alternative to the linked list.

@benjamingr
Copy link
Member

@legendecas

expansion of array is not constant time

You're right - it's not constant. It's amortized constant time which is not quite the same thing - it basically means "acts like O(1) for a large enough N since the expensive operations are split across many invocations" (happy to elaborate with a more formal definition if you prefer).

(Anyway a FixedQueue is the best of both worlds since it's also allocation friendly for resets)

@Mesteery Mesteery added perf_hooks Issues and PRs related to the implementation of the Performance Timing API. performance Issues and PRs related to the performance of Node.js. labels Feb 16, 2022
@jasnell
Copy link
Member

jasnell commented Feb 16, 2022

FixedQueue would likely be fine here.

@gioragutt
Copy link
Contributor

As a wise Benji once said - I'm taking a stab at this 🗡

nodejs-github-bot pushed a commit that referenced this issue Feb 20, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
bengl pushed a commit to bengl/node that referenced this issue Feb 21, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: nodejs#42004
Fix: nodejs#42024

PR-URL: nodejs#42032
Fixes: nodejs#42004
Fixes: nodejs#42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
bengl pushed a commit to bengl/node that referenced this issue Feb 21, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: nodejs#42004
Fix: nodejs#42024

PR-URL: nodejs#42032
Fixes: nodejs#42004
Fixes: nodejs#42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
bengl pushed a commit that referenced this issue Feb 21, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
bengl pushed a commit that referenced this issue Feb 21, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
bengl pushed a commit that referenced this issue Feb 22, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
danielleadams pushed a commit that referenced this issue Apr 21, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
danielleadams pushed a commit that referenced this issue Apr 24, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
danielleadams pushed a commit that referenced this issue Apr 24, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
danielleadams pushed a commit that referenced this issue Apr 24, 2022
Also order entries by startTime when calling getEntriesByType.

Fix: #42004
Fix: #42024

PR-URL: #42032
Fixes: #42004
Fixes: #42024
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
perf_hooks Issues and PRs related to the implementation of the Performance Timing API. performance Issues and PRs related to the performance of Node.js.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants