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

Safely ignore error stack trace #130

Open
H4ad opened this issue Oct 31, 2023 · 15 comments
Open

Safely ignore error stack trace #130

H4ad opened this issue Oct 31, 2023 · 15 comments

Comments

@H4ad
Copy link
Member

H4ad commented Oct 31, 2023

I saw this library https://github.com/isaacs/catcher and I decided to wrote a benchmark:

Doing some changes on bench/error.js, the result was:

name ops/sec samples
Error 337,720 64
Error (stackTraceLimit=0) 3,284,296 94
NodeError 283,026 99
NodeError (stackTraceLimit=0) 3,280,933 95
NodeError Range 214,825 92
NodeError Range (stackTraceLimit=0) 3,313,990 99
Code
const { createBenchmarkSuite } = require('../common')

const suite = createBenchmarkSuite('Node.js Error')

suite
  .add('Error', function () {
    try {
      new Error('test')
    } catch (e) { }
  })
  .add('Error (stackTraceLimit=0)', function () {
    const originalStackTraceLimit = Error.stackTraceLimit
    Error.stackTraceLimit = 0
    try {
      new Error('test')
    } catch (e) { }
    finally {
      Error.stackTraceLimit = originalStackTraceLimit;
    }
  })
  .add('NodeError', function () {
    try {
      new TypeError('test')
    } catch (e) { }
  })
  .add('NodeError (stackTraceLimit=0)', function () {
    const originalStackTraceLimit = Error.stackTraceLimit
    Error.stackTraceLimit = 0
    try {
      new TypeError('test')
    } catch (e) { }
    finally {
      Error.stackTraceLimit = originalStackTraceLimit;
    }
  })
  .add('NodeError Range', function () {
    try {
      new RangeError('test')
    } catch (e) { }
  })
  .add('NodeError Range (stackTraceLimit=0)', function () {
    const originalStackTraceLimit = Error.stackTraceLimit
    Error.stackTraceLimit = 0
    try {
      new RangeError('test')
    } catch (e) { }
    finally {
      Error.stackTraceLimit = originalStackTraceLimit;
    }
  })
  .run({ async: false })

Based on this assumption, maybe we can find places on Node where we can safely ignore the stackTraceLimit, using this search, I found some places:

@Uzlopak
Copy link
Contributor

Uzlopak commented Oct 31, 2023

Yes, makes sense.

We made imho good experience with this pattern. E.g. https://github.com/fastify/secure-json-parse/blob/4c9c7a6b7490e66406c9847b6583e173ab251f37/index.js#L100

@RafaelGSS
Copy link
Member

But, wouldn't it show no stacktraces at all? If so, this is unlikely to happen due to many reasons.

@H4ad
Copy link
Member Author

H4ad commented Nov 10, 2023

But, wouldn't it show no stacktraces at all?

Yep, it will be empty and will just print the error message.

If so, this is unlikely to happen due to many reasons.

What do you mean?


Also, I tried to refactor a bunch of places to introduce a function to abstract this pattern and it was not faster, I think for this optimization work, we should manually add Error.stackTraceLimit=0 for each piece of code.

The amount of redundant code I don't think it will worth to add for most cases, but I will continue explore to see if I found some place that worth to add it.

@CanadaHonk
Copy link
Member

I wonder if there are any hot paths which:

  • Use try without caring about a stacktrace
  • The try errors often (seems to not matter otherwise)
  • Called often for this to be limiting

If so, they could benefit greatly from this (since it is a ~10x speedup).

@H4ad
Copy link
Member Author

H4ad commented Nov 21, 2023

@CanadaHonk I tried using a helper function but it didn't help.

I also didn't find any hot paths, just places that could be optimized but they are not critical.

If you want to try this, you should add the try-catch in the same code that you want to optimize, a function will not help.

@RafaelGSS
Copy link
Member

Remember that in most cases, removing a stack trace may do more harm than good. They are quite useful.

@CanadaHonk
Copy link
Member

Agreed, the point here is that for try { ... } catch { ... }s where we do not use the error, we do not need to waste time generating the stack trace for them (afaict).

@CanadaHonk
Copy link
Member

CanadaHonk commented Nov 27, 2023

@H4ad I found an example use, although it isn't that useful for real world (I probably won't make it a PR unless requested): CanadaHonk/node@7a774cd

The idea shows some potential though, if a place was found which would have a real world benefit.

@H4ad
Copy link
Member Author

H4ad commented Nov 27, 2023

@CanadaHonk I think you should open a PR for this case, the improvements are good even though this is an edge-case and a deprecated function.

@tushar32
Copy link

By setting stackTraceLimit=0 or stackTraceLimit=2 will help to run unit tests faster becasue it throws error in multiple test caces . hence slows down test case?

@H4ad
Copy link
Member Author

H4ad commented Mar 11, 2024

@tushar32 It can help but you need to throw a lot of exceptions to this micro-opmization to be relevant.

@Uzlopak
Copy link
Contributor

Uzlopak commented Mar 11, 2024

On my machine: StackTrace off is about 1 mio errors/s and stacktrace at 10 is about 200k errors/s. You wont really feel it in local unit tests.

@lemire
Copy link
Member

lemire commented Mar 11, 2024

If you discard entirely stack traces, how much faster does Node run (on some interesting benchmarks)? That is, what is the overhead due to stack traces?

(And, yes, I am aware that Node will keep the stack traces generally speaking... I am asking the question to determine how interesting optimizing stack traces could be.)

@Uzlopak
Copy link
Contributor

Uzlopak commented Mar 11, 2024

@lemire

Maybe you find here some info?
fastify/fastify-error#103

@H4ad
Copy link
Member Author

H4ad commented Mar 11, 2024

Rafael created a benchmark to track the performance of throwing errors: https://github.com/RafaelGSS/nodejs-bench-operations/blob/main/v21/RESULTS-v21_7_1.md#nodejs-error

If we go with the simplest approach, creating another one with stackTraceLimit=0 should be enough to have an idea.

But we already had good optimizations on throwing errors in the latest releases, you can follow at #40

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants