First Key Optimization
First Key Optimization

Since the first key is used in every comparision, precompute its character position.

Notes
This optimization seems a little klunky. This could be redone with more agressive code modification to good advantage.

Need to review interactions with MergeMode.


Declare field position (keybeg, keylim)
Field declaration
Segment Source
  81:   char *keybeg;                 /* Start of first key. */
  82:   char *keylim;                 /* Limit of first key. */

Precompute first key for each record
Segment Element
Variable definition

 692:   struct keyfield *key = keyhead.next;

Segment Element
Code insertion

Notes
Note that this is essentially the same logic as the code for finding the beginning of a field in keycompare(), except this is optimized for one field lookup. The keycompare() version minimizes the number of times it inspects a key flag by inter-weaving the a and b versions for the same key.
 715:       /* Precompute the position of the first key for efficiency. */
 716:       if (key)
 717:         {
 718:           if (key->eword >= 0)
 719:             lines->lines[lines->used].keylim =
 720:               limfield (&lines->lines[lines->used], key);
 721:           else
 722:             lines->lines[lines->used].keylim = ptr;
 723: 
 724:           if (key->sword >= 0)
 725:             lines->lines[lines->used].keybeg =
 726:               begfield (&lines->lines[lines->used], key);
 727:           else
 728:             {
 729:               if (key->skipsblanks)
 730:                 while (blanks[UCHAR (*beg)])
 731:                   ++beg;
 732:               lines->lines[lines->used].keybeg = beg;
 733:             }
 734:           if (key->skipeblanks)
 735:             {
 736:               trim_trailing_blanks (lines->lines[lines->used].keybeg,
 737:                                     &lines->lines[lines->used].keylim);
 738:             }
 739:         }
 740:       else
 741:         {
 742:           lines->lines[lines->used].keybeg = 0;
 743:           lines->lines[lines->used].keylim = 0;
 744:         }
 745: 

Use precomputed field
Code insertion
Notes
The re-write for this is non-trivial. See the <Activity log> reports for details.

The use of iter here is curious. In keycompare(), it's only used to select the precomputed key positions on the first iteration. At the very least, it is being incremented too often - it should only be changed in the else clause near line 1027. However, because of the way a->keybeg and b->keybeg are established in findlines(), it may be completely redundant.

Segment Source
 994:   int diff = 0, iter = 0, lena, lenb;
 995: 
 996:   for (key = keyhead.next; key; key = key->next, ++iter)
 997:     {
 998:       ignore = key->ignore;
 999:       translate = (unsigned char *) key->translate;
1000: 
1001:       /* Find the beginning and limit of each field. */
1002:       if (iter || a->keybeg == NULL || b->keybeg == NULL)
1003:         {
1004:           if (key->eword >= 0)
1005:             lima = limfield (a, key), limb = limfield (b, key);
1006:           else
1007:             lima = a->text + a->length, limb = b->text + b->length;
1008: 
1009:           if (key->sword >= 0)
1010:             texta = begfield (a, key), textb = begfield (b, key);
1011:           else
1012:             {
1013:               texta = a->text, textb = b->text;
1014:               if (key->skipsblanks)
1015:                 {
1016:                   while (texta < lima && blanks[UCHAR (*texta)])
1017:                     ++texta;
1018:                   while (textb < limb && blanks[UCHAR (*textb)])
1019:                     ++textb;
1020:                 }
1021:             }
1022:         }
1023:       else
1024:         {
1025:           /* For the first iteration only, the key positions have
1026:              been precomputed for us. */
1027:           texta = a->keybeg, lima = a->keylim;
1028:           textb = b->keybeg, limb = b->keylim;
1029:         }