The question "why do we have setlinebuf(stdout)?" came up on the list recently, and by strange coincidence here it is causing more trouble: performance rather than usability this time. Just removing the line buffering alone results in a significant speedup.
The switch to use %lld rather than %f in obvious cases is another major speedup. Calling strlen() once on TT.s and using fwrite() rather than using printf() every time wouldn't be noticeable if the other two issues weren't fixed, but is a decent chunk of the remaining time at this point. GNU seq is still *another* 10x faster on my desktop, and I did experiment with inlining the trivial `n /= 10` integer-to-ascii algorithm (to at least remove some of the remaining printf() overhead) but the savings from that were quite small at this point and didn't seem worth the extra code. And while being 100x worse was a human-noticeable thing, being 10x worse than "basically instant even on very large inputs" doesn't seem likely to be something anyone will notice (and we can worry about that if/when they do). --- main.c | 1 - tests/seq.test | 2 ++ toys/lsb/seq.c | 21 ++++++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-)
From 94c847b8e8cdf8a0a5147c514fccacfd28d9c3c7 Mon Sep 17 00:00:00 2001 From: Elliott Hughes <[email protected]> Date: Fri, 11 Dec 2020 11:18:44 -0800 Subject: [PATCH] seq: fast path for integers. The question "why do we have setlinebuf(stdout)?" came up on the list recently, and by strange coincidence here it is causing more trouble: performance rather than usability this time. Just removing the line buffering alone results in a significant speedup. The switch to use %lld rather than %f in obvious cases is another major speedup. Calling strlen() once on TT.s and using fwrite() rather than using printf() every time wouldn't be noticeable if the other two issues weren't fixed, but is a decent chunk of the remaining time at this point. GNU seq is still *another* 10x faster on my desktop, and I did experiment with inlining the trivial `n /= 10` integer-to-ascii algorithm (to at least remove some of the remaining printf() overhead) but the savings from that were quite small at this point and didn't seem worth the extra code. And while being 100x worse was a human-noticeable thing, being 10x worse than "basically instant even on very large inputs" doesn't seem likely to be something anyone will notice (and we can worry about that if/when they do). --- main.c | 1 - tests/seq.test | 2 ++ toys/lsb/seq.c | 21 ++++++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index 25e4c472..0ae98ae3 100644 --- a/main.c +++ b/main.c @@ -102,7 +102,6 @@ void toy_singleinit(struct toy_list *which, char *argv[]) // Try user's locale, falling back to C.UTF-8 setlocale(LC_CTYPE, ""); if (!strcmp("UTF-8", nl_langinfo(CODESET))) setlocale(LC_CTYPE, "C.UTF-8"); - setlinebuf(stdout); } } diff --git a/tests/seq.test b/tests/seq.test index 15a208bb..9dcfcc3e 100755 --- a/tests/seq.test +++ b/tests/seq.test @@ -69,3 +69,5 @@ testing "invalid increment" "seq 1 1f 1 2>/dev/null || echo y" "y\n" "" "" # TODO: busybox fails this too, but GNU seems to not use double for large ints. #testing "too large for double" "seq -s, 9007199254740991 1 9007199254740992" "9007199254740992\n" "" "" + +testing "fast path" "seq 10000000 > /dev/null" "" "" "" diff --git a/toys/lsb/seq.c b/toys/lsb/seq.c index 988466b7..73b715aa 100644 --- a/toys/lsb/seq.c +++ b/toys/lsb/seq.c @@ -57,21 +57,31 @@ static double parsef(char *s) void seq_main(void) { double first = 1, increment = 1, last, dd; - int i; + long long firstl, incrementl, lastl; + int i, easy, slen; if (!TT.s) TT.s = "\n"; + slen = strlen(TT.s); + switch (toys.optc) { case 3: increment = parsef(toys.optargs[1]); case 2: first = parsef(*toys.optargs); default: last = parsef(toys.optargs[toys.optc-1]); } + // Can we avoid %f and just use printf("%lld") for a 10x speedup? + incrementl = increment; + firstl = first; + lastl = last; + easy = incrementl==increment && firstl==first && lastl==last && !FLAG(f) && + !TT.precision; + // Prepare format string with appropriate precision. Can't use %g because 1e6 - if (toys.optflags & FLAG_f) insanitize(TT.f); + if (FLAG(f)) insanitize(TT.f); else sprintf(TT.f = toybuf, "%%.%df", TT.precision); // Pad to largest width - if (toys.optflags & FLAG_w) { + if (FLAG(w)) { int len = 0; for (i=0; i<3; i++) { @@ -91,8 +101,9 @@ void seq_main(void) // Multiply to avoid accumulating rounding errors from increment. dd = first+i*increment; if ((increment<0 && dd<last) || (increment>0 && dd>last)) break; - if (i++) printf("%s", TT.s); - printf(TT.f, dd); + if (i++) fwrite(TT.s, slen, 1, stdout); + if (easy) printf("%lld", (long long) dd); + else printf(TT.f, dd); } if (i) printf("\n"); -- 2.29.2.684.gfbc64c5ab5-goog
_______________________________________________ Toybox mailing list [email protected] http://lists.landley.net/listinfo.cgi/toybox-landley.net
