DEV Community

Cover image for Say Goodbye to console.time() and Hello to performance.mark()!
Nathan G Bornstein
Nathan G Bornstein

Posted on

Say Goodbye to console.time() and Hello to performance.mark()!

As JavaScript developers, our primary goals can typically be summed up within three very broad concepts when it comes to our applications:

  1. Make the user's life easier
  2. Make the user's desires met
  3. Make the user's experiences efficient

While some may not agree with those 3 points as being the focal purpose in what they create (looking at you black hats), I'd wager that most developers would at least want a modicum of those three points blended into their blood, sweat and tears. My post today primarily locks on to the 3rd point, efficiency.

If you aren't already aware of the statistics for bounce rates in relation to the timing of the First Contentful Paint, then let me illustrate a graph to show you how fast n' dirty the times we're currently living in are:


A graph depicting generalized website bounce rates, with the earliest bounce rate occurring at 90% within a 1 to 5 second load time
Credit for image to Semrush


As you can see, in an extremely depressing fashion, if your application doesn't load within 1 to 5 seconds, a devastating percentage of your users are going to GTFO on outta there. Oh, you say your page is going to load at the 7 second mark? Kiss any CTA's goodbye, my friend.

Alright, have I scared you enough? Well I hope I have, because now we can finally get to what it is I want to talk about and stop with the background information like I'm some kind of YouTuber needing to increase their video duration.


LET'S GET TO IT


Since the inception of Node v 0.1, console.time() has been an extremely valuable method to determine how long a given process takes. This "process" could be a range of any number of things, from a specific function within an app to the entirety of that application altogether. The way that console.time() operates is within either a Node.js environment or within a browser. This isn't a core method within ECMAScript and therefore, doesn't run purely within JavaScript (but what does nowadays?).

However, as a baby that's freshly born grows and absorbs information like a sponge, so does technology progress. There are some quirks within console.time() that renders it inadequate for certain modern applications. Let's sum those up now:

  1. Inconsistent Precision Across Environments:
    When measuring performance locally using console.time(), Node is using the process.hrtime() method internally to assess your performance metric. Generally, browsers nowadays internally use performance.now() to gauge their own metrics via their own built-in devtools. This basically amounts to you checking timing on your own device vs what the user's given browser is going to implement for efficiency metrics and that difference can be staggering. Which leads us to our second point:

  2. Console Overhead:
    While I'm sure many of us have machines more than capable of benchmarking the latest graphically and process intensive metrics, there still comes a price with using your terminal to benchmark your application's performance. This rings especially true when it comes to shorter tasks and tighter loops. If you're trying to shave milliseconds off of some method you're developing, console.time() is going to leave you scratching your head when it performs within a browser context vs what your console informed you of.


And finally, the bright, shining beacon that led me to write this psychological vomit:


3. No Built-In Statistics:
That's right, within the Performance object, you're given a plethora of built-in methods to determine a wide variety of performance metrics. For console.time(), you're just measuring milliseconds on your own device without any additional context. That's like reading the headline to an article and then arguing with your friends to "just trust" you, "bro".


Now that I've been dragging console.time() through the mud, I hope that's been a sort of catalyst to show you how much better performance.mark() is in relation to all that smack talk. And best of all, Performance is a "native" process that works within both Node and practically all browsers! I mean, I guess console.time() does too, but it's better; just trust me, bro. However, instead of drawing on any more comparisons, as I've been doing, let me just speak on the performance of Performance:

  1. Performance is Non-Thread-Blocking:
    console.time() inherently thread-blocks within its processes (look, I know I just said I wasn't going to shit-talk console.time() anymore, but just stick with me, OK?). The way in which the Performance object works is quite elegant in its execution, via its own Performance API. Whenever you use Performance, it creates a high-resolution timestamp within the environment's monotonic clock and then registers that within JavaScript as a basic integer for later use, to determine the speed. And YES, that does mean you should test your application across all major browser environments using performance.mark(). However, that essentially amounts to an O(1) performance register within memory across ALL devices!

  2. SO many metrics:
    As I had mentioned previously, Performance offers an incredible number of metrics to finely tune precision for basically any quantitative measurement you're looking for. This deserves its own post by itself, but here's a brief summary of the major methods within it:

  • performance.now(): Precisely measures code duration via high-resolution timestamps, regardless of your machine's console fuckery, or pretty much any other external factors. Oh and, I know I keep saying "high-resolution timestamp", but all that means is a number within sub-millisecond precision. For comparisons' sake, Date.now() is within a millisecond's precision (I s2g, if you chastise me about comparisons one more time...).

This is a very general method, meaning you can plug just about any process, no matter how big or how small between it. Here's a working example of such:

const start = performance.now();
yourFunctionOrAppIDontKnow();
console.log(performance.now() - start, "sub-ms");
Enter fullscreen mode Exit fullscreen mode
  • performance.mark(label): This is essentially the same label you would use with console.time(); it marks the beginning and the end of the process you wish to benchmark. In use, it would look something similar to this:
performance.mark("startFetch");
await fetch("/api/data");
performance.mark("endFetch");
Enter fullscreen mode Exit fullscreen mode

It's important to keep in mind that this particular method does NOT do the actual performance metric, like performance.now() would do. All this method encapsulates are the beginning and the ending markers between your time comparison. This is useful for pinpointing specific processes within your application, if you don't wish to test the entirety of it. So with that, let's move on to see how to actually capture those specific metrics in conjunction with performance.mark(label):

  • performance.measure() Let's say we have a super sweet Fibonacci sequence that's tail-call optimized (I can hear the groans now). If we wanted to measure that using this new fandangled performance metric, we'd apply it as such:
function fib(n, a = 0, b = 1) {
  if (n === 0) return a;
  if (n === 1) return b;
  return fib(n - 1, b, a + b);
}

// start the timing
performance.mark("startFib");

// start the function
const result = fibTail(7);

// end the timing
performance.mark("endFib");

// measure the actual duration:
// `fibDuration` is the specific label given to performance.measure():
// `startFib` and `endFib` were given labels to performance.mark() earlier
performance.measure("fibDuration", "startFib", "endFib");

// retrieve the measurement
const measure = performance.getEntriesByName("fibDuration", "mark")[0]; // ?????

console.log(`fib(${n}) = ${result}`);
console.log(`Execution time: ${measure.duration.toFixed(3)} sub-ms`);
}
Enter fullscreen mode Exit fullscreen mode

But WHAT is this new performance.getEntriesByName() method I speak of?? Let's dive into it:

  • performance.getEntriesByName(): This method returns an array of PerformanceEntry objects, each with their own designated purpose or type. In our instance, we want the default "mark" type, as we've marked our function we wish to benchmark. If you don't provide an argument for that second type parameter, "mark" is what it defaults to.

There are many, many more methods contained within the Performance object, and for my eagle-eyed readers out there, you probably noticed the measure.duration() method in the previous code snippet. Since I don't have the bandwidth to continue elaborating on such things, you'll just have to figure that out on your own ;)


And that about sums it up, in terms of demonstrating the betterment of performance.mark() in relation toconsole.time(). In summary, I don't think console.time() is doo-doo ca-ca, but I do think that applications needing more rigid of constraints in their timing can 100% benefit from using the Performance object and its many methods.

If you made it this far, I Love You!
<3<3

Top comments (0)