Issue
In trying to debug a very rare bug in tests I'm writing, I decided to add a very homebrew tracing mechanism to my code. Basically I have
#tracer?: Array<string>;
myclass.setTracer(tracer?: Array<string>) {
this.#tracer = tracer;
}
#trace(msg: string) {
if (this.#tracer) {
this.#tracer.push(msg);
}
I can then add a call to setTracer(someArrayOfString)
and pepper my code with
this.#trace("my message");
and if I fail, print out the trace I have in someArrayOfStrings to try and figure out what went wrong.
However, this would seemingly have a performance hit, especially if I'm creating unique strings (i.e. interpolating variables).
I figure, I could probably do something like to avoid the string creation (though maybe I'm wrong about the optimizations)
(this.#tracer !== undefined) && this.#tracer.push(msg);
but I find peppering my code with that instead of the the "cleaner" this.#trace() call (that effectively does the same logical thing, but only after string has been created).
Is it possible to improve this? I assume others have thought of this, and perhaps have better solutions that I'd be willing to implement (just don't know them).
any recommendations would be appreciated.
Solution
I recommend using the empty function instead of any check for #trace
or #tracer
.
For example:
#trace = () => {}; /* always keep that is empy function */
setTracer (/*String[]*/ tracer) {
if (tracer) {
this.#trace = (msg) => tracer.push(msg);
} else {
this.#trace = () => {};
}
}
Below is my test code, assuming a production environment where you don't use setTracer
:
const LoopEffort = 100 * 1000 * 1000;
class TestRunner1 {
#trace = () => {}; /* always keep that is empy function */
setTracer (/*String[]*/ tracer) {
if (tracer) {
this.#trace = (msg) => tracer.push(msg);
} else {
this.#trace = () => {};
}
}
run (timeLabel) {
console.time(timeLabel);
for(let i = 0; i < LoopEffort; i++) {
this.#trace("OK");
}
console.timeEnd(timeLabel);
}
}
class TestRunner2 {
#trace = undefined;
setTracer (/*String[]*/ tracer) {
if (tracer) {
this.#trace = (msg) => tracer.push(msg);
} else {
this.#trace = undefined;
}
}
run (timeLabel) {
console.time(timeLabel);
for(let i = 0; i < LoopEffort; i++) {
this.#trace?.("OK");
}
console.timeEnd(timeLabel);
}
}
class TestRunner3 {
#tracer = undefined;
#trace (msg) {
if (this.#tracer) {
this.#tracer.push(msg);
}
}
setTracer (/*String[]*/ tracer) {
this.#tracer = tracer;
}
run (timeLabel) {
console.time(timeLabel);
for(let i = 0; i < LoopEffort; i++) {
this.#trace("OK");
}
console.timeEnd(timeLabel);
}
}
class TestRunner4 {
#tracer = undefined;
#trace (msg) {
this.#tracer?.push(msg);
}
setTracer (/*String[]*/ tracer) {
this.#tracer = tracer;
}
run (timeLabel) {
console.time(timeLabel);
for(let i = 0; i < LoopEffort; i++) {
this.#trace("OK");
}
console.timeEnd(timeLabel);
}
}
/* execute */
new TestRunner1().run("Using empty function");
new TestRunner2().run("Using optional chaining on #trace");
new TestRunner3().run("Check null of #tracer");
new TestRunner4().run("Using optional chaining on #tracer");
Using an empty function completely beats the other contenders!
Answered By - Henry Trần
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.