Benchmarking palindrome checkers in JavaScript

While working as a teaching assistant, curiosity struck. As a warmup exercise for class, students wrote a function to check if a given string is a palindrome in JavaScript. Not knowing many language features yet, most student solutions involved for loops.

function isPalindrome(str) {
for(var i = 0; i < str.length; i++) {
if (str[i] !== str[str.length - (i + 1)]) {
return false;
}
}
return true;
}

I completed the exercise with students and wrote a function that uses array methods:

function isPalindrome(str) {
return str === str.split("").reverse().join("");
}

Musing about performance

The solution that uses array methods is more terse, but is it more performant? Not knowing much about the implementation of JavaScript array methods, I wondered if the for loop method might be faster on larger strings which are not palindromes. My reasoning: it might perform the check faster if it only has to compare the first and last letter in some cases and not always transform an array several times as the array method does.

Benchmarking

To answer the question, I ran some benchmarks with Benchmark.js on both functions for the following cases:

  1. Small palindrome
  2. Big palindrome
  3. Small not palindrome
  4. Big not palindrome

The full benchmark suite script is available as a gist:

Originally, I wrote the script using uubench, but had a hard time intepreting the results. A friend also gave some valuable feedback that lead me to read more about benchmarking. That reading clarified the complicating factors and motivated me to switch to the Benchmark library.

Results

After learning how to read the results, they seemed to support my musing. Console output is below:

for loop, small word, is palindrome x 51,523,594 ops/sec ±0.30% (91 runs sampled)
array methods, small word, is palindrome x 2,260,466 ops/sec ±0.30% (91 runs sampled)
for loop, big word, is palindrome x 320,529 ops/sec ±0.23% (92 runs sampled)
array methods, big word, is palindrome x 32,742 ops/sec ±1.96% (88 runs sampled)
for loop, small word, not palindrome x 147,685,028 ops/sec ±0.60% (88 runs sampled)
array methods, small word, not palindrome x 1,687,614 ops/sec ±2.00% (89 runs sampled)
for loop, big word, not palindrome x 133,049,322 ops/sec ±3.59% (82 runs sampled)
array methods, big word, not palindrome x 59,128 ops/sec ±5.25% (82 runs sampled)
-------------
Fastest: for loop, small word, not palindrome
Slowest: array methods, big word, is palindrome

Following these results, I’m curious to explore the memory trade-off between each solution. Answering that question, though, would require proper profiling to understand which function is most memory intensive. I’m also interested in better understanding benchmarking to improve my confidence in building suites and intepreting results.