1
\$\begingroup\$

I am writing a script to count the number of rows in a textarea, and as it will be called on every keypress I want to make sure it runs as fast as possible. I'm not aiming for a general solution: we can assume that the textarea has a monospace font and enough horizontal space for exactly 80 characters.

I've probably solved this problem in 20 different ways, and this is by far the fastest solution I've come up with:

function countRows1(txt) {
    var i, len, rows = 0;
    txt = txt.split('\n');
    len = txt.length;
    for (i = 0; i < len; i++) {
        rows += Math.ceil(txt[i].length / 80) || 1;
    }
    return rows;
}

Here's a second solution, which is slightly faster in Firefox, but otherwise between a little and 5x slower:

function countRows2(txt) {
    var i = 0, nextBreak, rows = 0;
    while ((nextBreak = txt.indexOf('\n', i)) !== -1) {
        rows += Math.ceil((nextBreak - i) / 80) || 1;
        i = nextBreak + 1;
    }
    return rows + (Math.ceil((txt.length - i) / 80) || 1);
}

The performance doesn't make any sense to me - countRows2 does the same thing as countRows1, except it doesn't create any unnecessary strings or arrays. Is indexOf with a start position just really slow, or is there some optimization only the first method is picking up on?

Finally, here's one more method, which is much slower in every browser I've tested, but beautifully concise:

function countRows3(txt) {
    return txt.match(/\n?[^\n]{1,80}|\n/g).length;
}

Here's the jsPerf I'm using to compare the three.

So I've really got three questions:

  1. Why is countRows2 slower than countRows1?
  2. Is there any way to optimize the regex in countRows3 so the performance is comparable to the first two?
  3. Is there any other way to speed up this code?

ps: I can't just do something like el.scrollHeight, because I need it to shrink as text is deleted. Also, I don't want to remember the previous count and just calculate the difference, because some changes will inevitably be missed and a differential script will never right itself.

\$\endgroup\$
2
  • \$\begingroup\$ Not that the performance difference will be noticable - but do you really need to do this on every keypress event? If not consider throttling the calls to maybe one call every (half) second. \$\endgroup\$ Commented Aug 23, 2012 at 2:41
  • \$\begingroup\$ @ThiefMaster well it's for a personal page, so I can do whatever I want. On every keypress would make it more responsive though, and I'm not really seeing performance issues as-is. I guess I'm more curious to see where this can go, than to solve a pressing problem. \$\endgroup\$
    – delete me
    Commented Aug 23, 2012 at 2:52

1 Answer 1

2
\$\begingroup\$

What kind of performance are you getting? Is it acceptable. If it's not, then I'd look for changing your algorithm, and not minor tweaks. In particular, I'd look into NOT splitting the string and recalclating each time. Instead keep track of the state (what line and the number of characters on that line, whether it's a paste or a delete), and do the right thing based upon that. Adding an additional character at the end of a 5k block of text, adds one more line at most, if the last line was 1 character long, then unless the key being pressed is the enter key, it doesn't even do that.

But before doing anything, sit down and see how well your existing function works, for your typcial data, and then for some extreme data.

\$\endgroup\$
2
  • \$\begingroup\$ As I said in the question, I'd rather not take a differential approach, as it reacts very poorly to common failures like missing a single edit. You're 100% right about the testing though - I did test it, and performance has never faltered. So I guess this isn't a pressing problem, but I still want to see if I can speed it up, for slower machines, even extremer cases, etc. \$\endgroup\$
    – delete me
    Commented Aug 23, 2012 at 2:46
  • \$\begingroup\$ Also, really good point about the algorithm. This was actually one of the clearest times I've been able to see my code improve with a better algorithm - first I tried looping over each character, then each line, then this approach, and each time I saw an order of magnitude improvement. I'm not sure where I can go from here though. \$\endgroup\$
    – delete me
    Commented Aug 23, 2012 at 2:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.