7

Programming is about making decisions about how to implement any piece of code. Depending of such decisions, the code will be more or less readable, efficient, complex, etc. A common decision is also about to do it more or less idiomatic, that is, using specific statements or your programming language or paradigm.

As a proof of concept I have developed two code snippets, in Javascript, to analyze the performance. The goal is to generate a string in the form tagA|tagB|tagC where then number of tagX is random and the suffixes A, B, C are random integers. Moreover, tagX can not be repeated.

First implementation is more idiomatic, whereas the second one is more traditional. Next the code snippets of each one:

Idiomatic:

performance.mark('A');

let tags = new Set(Array.from({length: tagCount}, () => Math.floor(Math.random() * 10)));
tags = Array.from(tags).map(x => `tag${x}`).join('|');

performance.mark('B');

Traditional

performance.mark('C');

let tagset = new Set();
let tagstr = "";

for (let tag=0; tag<tagCount; tag++) {
    tagset.add(Math.floor(Math.random() * 10));
}

tagset.forEach((tag) => {
    tagstr += 'tag' + tag + '|'
});

tagstr = tagstr.slice(0, -1);

performance.mark('D');

To measure the performance I have used the Performance Timing API

The results are a bit surpraising, at least for me. Note how the traditional way is so much efficient than the other one.

Idiomatic: 0.436535
Traditional: 0.048177

I have repeated the experiment several times with similar results

Idiomatic way looks pretty cool and shorter, but it is less efficient and hard to read.

What do you think about it? Does it worth using idiomatic programming instead of traditional? Is there a general answer or it is strongly dependent of each case?

What about code readability and complexity?

EDITED: tagX can not be repeated.

EDITED: just for reference, I have uploaded the complete code to Github: https://github.com/aecostas/benchmark-js-looping

Andrés
  • 719
  • 1
  • 4
  • 21
  • Why not use simply : `let tags = Array.from({length: tagCount}, () => 'tag' + Math.floor(Math.random() * 10)).join('|');` – Troopers Feb 01 '18 at 10:20
  • Because `tagX` cannot be repeated. I have edit the post to make this point clear. Anyway, looks like `Array.from` is very expensive in terms of CPU usage instead of the classical `for` – Andrés Feb 01 '18 at 10:39
  • 5
    I think you answered the question already. It's less efficient *and* hard to read. I think you should always go for readability first and maybe change the code in something that's harder to read if performance becomes an issue. If this code had neither performance nor readability, and its only (?) benefit is that it looks cool, than the only valid reason for using it, is if you're a 14yo script kiddie and want to impress some friends. – GolezTrol Feb 01 '18 at 10:50
  • Personally, I think the question would fit more for software-engineering or code-review than for stack overflow. The questions asked lend themselves mainly towards opinions – Icepickle Feb 01 '18 at 10:52
  • 1
    What does “more idiomatic” mean here? Neither of these is how you’d want to write anything in practice, and your benchmark is pretty suspect besides. Did you run these as part of the same script, and only once? What’s `tagCount`? Is it realistic? – Ry- Feb 01 '18 at 10:52
  • Yes, I have repetead the experiment several times with similar results. I will update the code snipped with that. What to you think the correct implementation would be in practice? – Andrés Feb 01 '18 at 11:08
  • I think your code snippet is not really idiomatic.. It is more a one liner of declare and initialize a `Set`. There are other idiomatic features like [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) – Roman Feb 01 '18 at 11:33
  • NB: the practical meaning of `tagCount` is a bit lost. Because of the uniqueness requirement, you'll not get that many tags. Would it not make more sense if `tagCount` were to be the number of tags in the final result, after ensuring uniqueness? – trincot Feb 01 '18 at 12:35
  • @trincot you are right, it is a bit confussing. In my case the definition would be "the maximun number of tags". Your suggestion sounds interesting. It does not fit the requirements at this moment, but it is worth considering it. Thank you! – Andrés Feb 02 '18 at 15:21

1 Answers1

6

Idiomatic way looks pretty cool and shorter, but it is less efficient and hard to read.

If people familiar with the language find it hard to read, it’s not “idiomatic”. (You might be going by one definition of the word – “using many idiom¹s” – but this isn’t what people mean when they refer to idiomatic code, though it’s true that Array.from({length}, fn), for example, is an idiom¹ for filling an array based on a function in JavaScript. Rather, it’s code that’s written the way users of the language expect.) You could get it closer by naming some stuff:

const tagNumbers = Array.from(
    {length: tagCount},
    () => Math.floor(Math.random() * 10),
);

const uniqueTagNumbers = Array.from(new Set(tagNumbers));

const tagString =
    uniqueTagNumbers
        .map(n => 'tag' + n)
        .join('|');

and by making use of the type of utility functions that would exist in practice:

import generate from '…';
import unique from '…';

const getRandomTag = () =>
    Math.random() * 10 | 0;

const tags =
    generate(tagCount, getRandomTag);

const tagString =
    unique(tags)
        .map(n => 'tag' + n)
        .join('|');

There’s only so far you can go with toy examples, though.

As for performance: not everything has to be micro-optimized. Your two examples here have the same asymptotic time and space complexity, and that’s enough for many cases. You’re writing in JavaScript because its language features let you express yourself better² at the cost of performance, after all. And when it does come to optimization, you’ll probably want to write more accurate benchmarks (which is pretty hard in Node!) – e.g. try reordering those measurements.

¹ in the sense of being an expression that users of the language are aware means something without the expression itself conveying that clearly.
² or because someone else or some other group of people thought that and now you’re stuck working on their code, or maybe it was for some reason other than expressiveness per se, or the aforementioned people were in that same boat³, …
³ oh hey an idiom

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Thanks for introducing the asymptotic time and space complexity concepts! They complement very well my question – Andrés Feb 02 '18 at 08:25