Javascript is fast. Really fast - at least if it´s running in a modern browser. So, in general you should not care too much about performance. But when it comes to text processing, things might be worth a closer look:
- are there impacts of the way we manipulate strings? Memory managmenet can cause serious bottlenecks
- Do the loops make any difference?
I really did not know, so I did some performance testing with interesting results that I want to share here.
Do some tests
My test case was quite simple: Add an "x" to each line of a text of 251 lines. Import of the file was done before measurement, so reading the text into a variable was not part of the test. I used the performance-API on chrome, which is intentionally a bit reduced in precision, but delivers reasonable results if you repeat your task often enough. In this case, I repeated the task 10.000 times.
So, this was my first code (count = 10_000, 251 lines):
for (n = 0; n < count; n++) {
res1 = text.split("\n").map(line => line + "x").join("\n")
} // Time 1 = 190ms, average 0.075 us/line
So, here we split the text into an array, convert the array and join the lines again finally. So we get the initial text with x added to every line. And performance is amazing! This are 1.5 Mio. text operations done in 0.19s, each operation was done in about 0,075 Microseconds on my fairly old machine.
But could we do better? Array manipulation might include some memory shifting. This was my second try:
for (n = 0; n < count; n++) {
ar = []
text.split("\n").forEach(line => ar.push(line + "x"))
res2 = ar.join("\n")
} // Time 2 = 178ms, average 0.071 us/line
Instead of map() I used array.push() to build the array, but this was only slightly faster.
The next try was to avoid join()
by adding the string manually:
for (n = 0; n < count; n++) {
res3 = ""
text.split("\n").forEach(line => res3 += line + "x\n")
} // 82ms, average 0.032 us/line
This was a big jump: It seems, string concatenation is really fast, cutting the time by more than 50%.
But what about the loops? ferEach does all the heavy lifting, so we should try it out:
for (n = 0; n < count; n++) {
lines = text.split("\n"); res4 = ""
for (i = 0; i < lines.length; i++)
res4 += lines[i] + "x\n"
} // Time 4 = 33ms, average 0.013 us/line
This was a suprise: The good old loop performs much better! Compared to the inital loop, this was 6 times faster! Amazing!.
But well, this looks a bit oldfashioned, so how about the modern style? See the results:
This is for .. in
for (n = 0; n < count; n++) {
lines = text.split("\n"); res5 = ""
for (i in lines)
res5 += lines[i] + "x\n"
} // Time 5 = 172ms, average 0.068 us/line
and for .. of
for (n = 0; n < count; n++) {
lines = text.split("\n"); res6 = ""
for (line of lines)
res6 += line + "x\n"
} // Time 6 = 36ms, average 0.015 us/line
So, finally we are back in the winning row, but results are really surprising: Just changing the loop style can slow you down by a factor of 5-6 (may be different on other machines). I added the code at the end of this post. The results will be different on your machine, as they are also depending much on the processor architecture.
There is absolutely no general rule. Things are much depending on the implementation details, and can be different on different browsers. But as you might see: It is worth to do some performance testing to get better results.
If you want to try out, here is a working example
Top comments (14)
Not sure how these results are obtained. Maybe you should also publish the code you used to test.
I Tried to reproduce using the information you gave us and with a few new approaches: see this snippet. The results are different.
Results will greatly depend on your machine and the browser. I added my test code at the end of the post so you can try the tests I did.
Right, so you're telling us you tested a text with 4625 lines, but in the snippet you are testing a text with 251 lines - the result of which you use here.
Oh, sorry, my fault! 4625 was the file length, not the line count. I changed the text, thank you for the hint!
You will find that results will greatly change on different machines. On and Intel(R) Core(TM) i7-1185G7 for..of was the winner, but the speed factor was only 2.7 to the slowest result.
Ok, both cases are now covered in my snippet. And yes, it (ofcourse) also depends on the used hardware.
BTW I would advice to always use semicolons for line ends in EcmaScript.
I have done a lot coding in C and Pascal, but did not miss the semicolons for any reason. Beside of personal taste, is there any technical reason to use semicolons?
Most of the time it's not relevant. I've coded
Java/ECMAScript since its conception (besides pascal, c, c++ and c#). Still every now and then I run into a problem because of the interpreters' automatic semicolon insertion *). So I force myself to use them.*) for example
Maybe
Might be worth a try, too.
In my setup regEx-replace was about 20% slower than the slowest solution, which is still not bad. Things might change if the task get´s more complicated, but in general I found that regEx performance is quite reasonable, Not top of the list, but also not much slower than hand optimized code. If you take into account that RegEx expressions can be quite complex, this is still an amazing result.
Thank you for the hint, I´ll add this to the post.
Yes 'good old loop performs much better' ;-)
VERY insightful post. Performance testing under a particular workload is a great way to ensure more positive UX.
Sure, but we should also look for the numbers. A delay of 50 ms will hardly be recognized by the user, which could mean:
Often it is not easy to find the reason for slow interactions. Doing things as simple as possible could be a good advice to reduce workload.
Yep.. small changes, big results.