on master help sign in
Improve string primitive performance

strlen is quite expensive when strings become moderately sized (e.g. processing a large input file), and we need to do it in most operations to work out how much to allocate, so instead only calculate it when creating a new string and cache it in the closure.

commitc0463b7ef4e36446da1152cc175f48b35e2bc87e authorEdwin Brady <ecb10@st-andrews.ac.uk> dateSun 18 Jun 2017, 20:03 (9 years ago) parent9dc9ecb
3 files changed+88−44
rts/idris_gc.c+1−1 rts/idris_rts.c+77−41 rts/idris_rts.h+10−2
rts/idris_gc.c+1−1
@@ -24,7 +24,7 @@ VAL copy(VM* vm, VAL x) {
2424 cl = MKFLOATc(vm, x->info.f);
2525 break;
2626 case CT_STRING:
27− cl = MKSTRc(vm, x->info.str);
27+ cl = MKSTRclen(vm, x->info.str.str, x->info.str.len);
2828 break;
2929 case CT_STROFFSET:
3030 cl = MKSTROFFc(vm, x->info.str_offset);
rts/idris_rts.c+77−41
@@ -285,19 +285,27 @@ VAL MKSTR(VM* vm, const char* str) {
285285 Closure* cl = allocate(sizeof(Closure) + // Type) + sizeof(char*) +
286286 sizeof(char)*len, 0);
287287 SETTY(cl, CT_STRING);
288− cl -> info.str = (char*)cl + sizeof(Closure);
288+ cl->info.str.str = (char*)cl + sizeof(Closure);
289289 if (str == NULL) {
290− cl->info.str = NULL;
290+ cl->info.str.str = NULL;
291291 } else {
292− strcpy(cl -> info.str, str);
292+ strcpy(cl->info.str.str, str);
293293 }
294+ cl->info.str.len = len > 0 ? len : 0;
294295 return cl;
295296}
296297
297298char* GETSTROFF(VAL stroff) {
298299 // Assume STROFF
299300 StrOffset* root = stroff->info.str_offset;
300− return (root->str->info.str + root->offset);
301+ return (root->str->info.str.str + root->offset);
302+}
303+
304+size_t GETSTROFFLEN(VAL stroff) {
305+ // Assume STROFF
306+ // we're working in char* here so no worries about utf8 char length
307+ StrOffset* root = stroff->info.str_offset;
308+ return (root->str->info.str.len - root->offset);
301309}
302310
303311VAL MKCDATA(VM* vm, CHeapItem * item) {
@@ -342,12 +350,26 @@ VAL MKFLOATc(VM* vm, double val) {
342350}
343351
344352VAL MKSTRc(VM* vm, char* str) {
353+ int len = strlen(str);
354+
355+ Closure* cl = allocate(sizeof(Closure) + // Type) + sizeof(char*) +
356+ sizeof(char)*len+1, 1);
357+ SETTY(cl, CT_STRING);
358+ cl -> info.str.str = (char*)cl + sizeof(Closure);
359+
360+ strcpy(cl->info.str.str, str);
361+ cl->info.str.len = len;
362+ return cl;
363+}
364+
365+VAL MKSTRclen(VM* vm, char* str, int len) {
345366 Closure* cl = allocate(sizeof(Closure) + // Type) + sizeof(char*) +
346− sizeof(char)*strlen(str)+1, 1);
367+ sizeof(char)*len+1, 1);
347368 SETTY(cl, CT_STRING);
348− cl -> info.str = (char*)cl + sizeof(Closure);
369+ cl -> info.str.str = (char*)cl + sizeof(Closure);
349370
350− strcpy(cl -> info.str, str);
371+ strcpy(cl->info.str.str, str);
372+ cl->info.str.len = len;
351373 return cl;
352374}
353375
@@ -428,7 +450,7 @@ void dumpVal(VAL v) {
428450 printf("] ");
429451 break;
430452 case CT_STRING:
431− printf("STR[%s]", v->info.str);
453+ printf("STR[%s]", v->info.str.str);
432454 break;
433455 case CT_FWD:
434456 printf("CT_FWD ");
@@ -490,8 +512,9 @@ VAL idris_castIntStr(VM* vm, VAL i) {
490512 int x = (int) GETINT(i);
491513 Closure* cl = allocate(sizeof(Closure) + sizeof(char)*16, 0);
492514 SETTY(cl, CT_STRING);
493− cl -> info.str = (char*)cl + sizeof(Closure);
494− sprintf(cl -> info.str, "%d", x);
515+ cl->info.str.str = (char*)cl + sizeof(Closure);
516+ sprintf(cl->info.str.str, "%d", x);
517+ cl->info.str.len = strlen(cl->info.str.str);
495518 return cl;
496519}
497520
@@ -503,32 +526,33 @@ VAL idris_castBitsStr(VM* vm, VAL i) {
503526 case CT_BITS8:
504527 // max length 8 bit unsigned int str 3 chars (256)
505528 cl = allocate(sizeof(Closure) + sizeof(char)*4, 0);
506− cl->info.str = (char*)cl + sizeof(Closure);
507− sprintf(cl->info.str, "%" PRIu8, (uint8_t)i->info.bits8);
529+ cl->info.str.str = (char*)cl + sizeof(Closure);
530+ sprintf(cl->info.str.str, "%" PRIu8, (uint8_t)i->info.bits8);
508531 break;
509532 case CT_BITS16:
510533 // max length 16 bit unsigned int str 5 chars (65,535)
511534 cl = allocate(sizeof(Closure) + sizeof(char)*6, 0);
512− cl->info.str = (char*)cl + sizeof(Closure);
513− sprintf(cl->info.str, "%" PRIu16, (uint16_t)i->info.bits16);
535+ cl->info.str.str = (char*)cl + sizeof(Closure);
536+ sprintf(cl->info.str.str, "%" PRIu16, (uint16_t)i->info.bits16);
514537 break;
515538 case CT_BITS32:
516539 // max length 32 bit unsigned int str 10 chars (4,294,967,295)
517540 cl = allocate(sizeof(Closure) + sizeof(char)*11, 0);
518− cl->info.str = (char*)cl + sizeof(Closure);
519− sprintf(cl->info.str, "%" PRIu32, (uint32_t)i->info.bits32);
541+ cl->info.str.str = (char*)cl + sizeof(Closure);
542+ sprintf(cl->info.str.str, "%" PRIu32, (uint32_t)i->info.bits32);
520543 break;
521544 case CT_BITS64:
522545 // max length 64 bit unsigned int str 20 chars (18,446,744,073,709,551,615)
523546 cl = allocate(sizeof(Closure) + sizeof(char)*21, 0);
524− cl->info.str = (char*)cl + sizeof(Closure);
525− sprintf(cl->info.str, "%" PRIu64, (uint64_t)i->info.bits64);
547+ cl->info.str.str = (char*)cl + sizeof(Closure);
548+ sprintf(cl->info.str.str, "%" PRIu64, (uint64_t)i->info.bits64);
526549 break;
527550 default:
528551 fprintf(stderr, "Fatal Error: ClosureType %d, not an integer type", ty);
529552 exit(EXIT_FAILURE);
530553 }
531554
555+ cl->info.str.len = strlen(cl->info.str.str);
532556 SETTY(cl, CT_STRING);
533557 return cl;
534558}
@@ -545,8 +569,10 @@ VAL idris_castStrInt(VM* vm, VAL i) {
545569VAL idris_castFloatStr(VM* vm, VAL i) {
546570 Closure* cl = allocate(sizeof(Closure) + sizeof(char)*32, 0);
547571 SETTY(cl, CT_STRING);
548− cl -> info.str = (char*)cl + sizeof(Closure);
549− snprintf(cl -> info.str, 32, "%.16g", GETFLOAT(i));
572+ cl->info.str.str = (char*)cl + sizeof(Closure);
573+ snprintf(cl->info.str.str, 32, "%.16g", GETFLOAT(i));
574+
575+ cl->info.str.len = strlen(cl->info.str.str);
550576 return cl;
551577}
552578
@@ -559,11 +585,13 @@ VAL idris_concat(VM* vm, VAL l, VAL r) {
559585 char *ls = GETSTR(l);
560586 // dumpVal(l);
561587 // printf("\n");
562− Closure* cl = allocate(sizeof(Closure) + strlen(ls) + strlen(rs) + 1, 0);
588+ Closure* cl = allocate(sizeof(Closure) + GETSTRLEN(l) + GETSTRLEN(r) + 1, 0);
563589 SETTY(cl, CT_STRING);
564− cl -> info.str = (char*)cl + sizeof(Closure);
565− strcpy(cl -> info.str, ls);
566− strcat(cl -> info.str, rs);
590+ cl->info.str.str = (char*)cl + sizeof(Closure);
591+ strcpy(cl->info.str.str, ls);
592+ strcat(cl->info.str.str, rs);
593+
594+ cl->info.str.len = GETSTRLEN(l) + GETSTRLEN(r);
567595 return cl;
568596}
569597
@@ -670,21 +698,26 @@ VAL idris_strTail(VM* vm, VAL str) {
670698VAL idris_strCons(VM* vm, VAL x, VAL xs) {
671699 char *xstr = GETSTR(xs);
672700 int xval = GETINT(x);
701+ int xlen = GETSTRLEN(xs);
702+
673703 if ((xval & 0x80) == 0) { // ASCII char
674704 Closure* cl = allocate(sizeof(Closure) +
675− strlen(xstr) + 2, 0);
705+ xlen + 2, 0);
676706 SETTY(cl, CT_STRING);
677− cl -> info.str = (char*)cl + sizeof(Closure);
678− cl -> info.str[0] = (char)(GETINT(x));
679− strcpy(cl -> info.str+1, xstr);
707+ cl->info.str.str = (char*)cl + sizeof(Closure);
708+ cl->info.str.str[0] = (char)(GETINT(x));
709+ strcpy(cl->info.str.str+1, xstr);
710+ cl->info.str.len = xlen + 1;
680711 return cl;
681712 } else {
682713 char *init = idris_utf8_fromChar(xval);
683− Closure* cl = allocate(sizeof(Closure) + strlen(init) + strlen(xstr) + 1, 0);
714+ int newlen = strlen(init) + xlen;
715+ Closure* cl = allocate(sizeof(Closure) + newlen + 1, 0);
684716 SETTY(cl, CT_STRING);
685− cl -> info.str = (char*)cl + sizeof(Closure);
686− strcpy(cl -> info.str, init);
687− strcat(cl -> info.str, xstr);
717+ cl->info.str.str = (char*)cl + sizeof(Closure);
718+ strcpy(cl->info.str.str, init);
719+ strcat(cl->info.str.str, xstr);
720+ cl->info.str.len = newlen;
688721 free(init);
689722 return cl;
690723 }
@@ -701,7 +734,7 @@ VAL idris_substr(VM* vm, VAL offset, VAL length, VAL str) {
701734 char* str_val = GETSTR(str);
702735
703736 // If the substring is a suffix, use idris_strShift to avoid reallocating
704− if (offset_val + length_val >= strlen(str_val)) {
737+ if (offset_val + length_val >= GETSTRLEN(str)) {
705738 return idris_strShift(vm, str, offset_val);
706739 }
707740 else {
@@ -709,20 +742,23 @@ VAL idris_substr(VM* vm, VAL offset, VAL length, VAL str) {
709742 char *end = idris_utf8_advance(start, length_val);
710743 Closure* newstr = allocate(sizeof(Closure) + (end - start) +1, 0);
711744 SETTY(newstr, CT_STRING);
712− newstr -> info.str = (char*)newstr + sizeof(Closure);
713− memcpy(newstr -> info.str, start, end - start);
714− *(newstr -> info.str + (end - start) + 1) = '\0';
745+ newstr->info.str.str = (char*)newstr + sizeof(Closure);
746+ memcpy(newstr->info.str.str, start, end - start);
747+ *(newstr->info.str.str + (end - start) + 1) = '\0';
748+ newstr->info.str.len = strlen(newstr->info.str.str);
715749 return newstr;
716750 }
717751}
718752
719753VAL idris_strRev(VM* vm, VAL str) {
720754 char *xstr = GETSTR(str);
721− Closure* cl = allocate(sizeof(Closure) +
722− strlen(xstr) + 1, 0);
755+ int xlen = GETSTRLEN(str);
756+
757+ Closure* cl = allocate(sizeof(Closure) + xlen + 1, 0);
723758 SETTY(cl, CT_STRING);
724− cl->info.str = (char*)cl + sizeof(Closure);
725− idris_utf8_rev(xstr, cl->info.str);
759+ cl->info.str.str = (char*)cl + sizeof(Closure);
760+ cl->info.str.len = xlen;
761+ idris_utf8_rev(xstr, cl->info.str.str);
726762 return cl;
727763}
728764
@@ -832,7 +868,7 @@ VAL doCopyTo(VM* vm, VAL x) {
832868 cl = MKFLOATc(vm, x->info.f);
833869 break;
834870 case CT_STRING:
835− cl = MKSTRc(vm, x->info.str);
871+ cl = MKSTRc(vm, x->info.str.str);
836872 break;
837873 case CT_BIGINT:
838874 cl = MKBIGMc(vm, x->info.ptr);
rts/idris_rts.h+10−2
@@ -42,6 +42,11 @@ typedef struct {
4242 size_t offset;
4343} StrOffset;
4444
45+typedef struct {
46+ char* str;
47+ size_t len; // Cached strlen (we do 'strlen' a lot)
48+} String;
49+
4550// A foreign pointer, managed by the idris GC
4651typedef struct {
4752 size_t size;
@@ -59,7 +64,7 @@ typedef struct Closure {
5964 con c;
6065 int i;
6166 double f;
62− char* str;
67+ String str;
6368 StrOffset* str_offset;
6469 void* ptr;
6570 uint8_t bits8;
@@ -182,7 +187,8 @@ typedef void(*func)(VM*, VAL*);
182187#define ALIGN(__p, __alignment) ((__p + __alignment - 1) & ~(__alignment - 1))
183188
184189// Retrieving values
185−#define GETSTR(x) (ISSTR(x) ? (((VAL)(x))->info.str) : GETSTROFF(x))
190+#define GETSTR(x) (ISSTR(x) ? (((VAL)(x))->info.str.str) : GETSTROFF(x))
191+#define GETSTRLEN(x) (ISSTR(x) ? (((VAL)(x))->info.str.len) : GETSTROFFLEN(x))
186192#define GETPTR(x) (((VAL)(x))->info.ptr)
187193#define GETMPTR(x) (((VAL)(x))->info.mptr->data)
188194#define GETFLOAT(x) (((VAL)(x))->info.f)
@@ -255,11 +261,13 @@ VAL MKCDATA(VM* vm, CHeapItem * item);
255261VAL MKFLOATc(VM* vm, double val);
256262VAL MKSTROFFc(VM* vm, StrOffset* off);
257263VAL MKSTRc(VM* vm, char* str);
264+VAL MKSTRclen(VM* vm, char* str, int len);
258265VAL MKPTRc(VM* vm, void* ptr);
259266VAL MKMPTRc(VM* vm, void* ptr, size_t size);
260267VAL MKCDATAc(VM* vm, CHeapItem * item);
261268
262269char* GETSTROFF(VAL stroff);
270+size_t GETSTROFFLEN(VAL stroff);
263271
264272// #define SETTAG(x, a) (x)->info.c.tag = (a)
265273#define SETARG(x, i, a) ((x)->info.c.args)[i] = ((VAL)(a))