When the caller knows the smaller string length, memcmp and strncmp are
functionally equivalent.  Since memcmp need not watch each byte for a NULL
terminator, it often compares a CPU word at a time for better performance.  The
attached patch changes use of strncmp to memcmp where we have the length of the
shorter string.  I was most interested in the varlena.c instances, but I tried
to find all applicable call sites.  To benchmark it, I used the attached
"bench-texteq.sql".  This patch improved my 5-run average timing of the SELECT
from 65.8s to 56.9s, a 13% improvement.  I can't think of a case where the
change should be pessimal.

Thanks,
nm
*** a/contrib/hstore/hstore_io.c
--- b/contrib/hstore/hstore_io.c
***************
*** 280,288 **** comparePairs(const void *a, const void *b)
  {
        if (((Pairs *) a)->keylen == ((Pairs *) b)->keylen)
        {
!               int                     res = strncmp(((Pairs *) a)->key,
!                                                                 ((Pairs *) 
b)->key,
!                                                                 ((Pairs *) 
a)->keylen);
  
                if (res)
                        return res;
--- 280,288 ----
  {
        if (((Pairs *) a)->keylen == ((Pairs *) b)->keylen)
        {
!               int                     res = memcmp(((Pairs *) a)->key,
!                                                                ((Pairs *) 
b)->key,
!                                                                ((Pairs *) 
a)->keylen);
  
                if (res)
                        return res;
***************
*** 324,330 **** hstoreUniquePairs(Pairs *a, int4 l, int4 *buflen)
        while (ptr - a < l)
        {
                if (ptr->keylen == res->keylen &&
!                       strncmp(ptr->key, res->key, res->keylen) == 0)
                {
                        if (ptr->needfree)
                        {
--- 324,330 ----
        while (ptr - a < l)
        {
                if (ptr->keylen == res->keylen &&
!                       memcmp(ptr->key, res->key, res->keylen) == 0)
                {
                        if (ptr->needfree)
                        {
*** a/contrib/hstore/hstore_op.c
--- b/contrib/hstore/hstore_op.c
***************
*** 49,55 **** hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen)
                stopMiddle = stopLow + (stopHigh - stopLow) / 2;
  
                if (HS_KEYLEN(entries, stopMiddle) == keylen)
!                       difference = strncmp(HS_KEY(entries, base, stopMiddle), 
key, keylen);
                else
                        difference = (HS_KEYLEN(entries, stopMiddle) > keylen) 
? 1 : -1;
  
--- 49,55 ----
                stopMiddle = stopLow + (stopHigh - stopLow) / 2;
  
                if (HS_KEYLEN(entries, stopMiddle) == keylen)
!                       difference = memcmp(HS_KEY(entries, base, stopMiddle), 
key, keylen);
                else
                        difference = (HS_KEYLEN(entries, stopMiddle) > keylen) 
? 1 : -1;
  
***************
*** 263,269 **** hstore_delete(PG_FUNCTION_ARGS)
                int                     len = HS_KEYLEN(es, i);
                char       *ptrs = HS_KEY(es, bufs, i);
  
!               if (!(len == keylen && strncmp(ptrs, keyptr, keylen) == 0))
                {
                        int                     vallen = HS_VALLEN(es, i);
  
--- 263,269 ----
                int                     len = HS_KEYLEN(es, i);
                char       *ptrs = HS_KEY(es, bufs, i);
  
!               if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
                {
                        int                     vallen = HS_VALLEN(es, i);
  
***************
*** 331,339 **** hstore_delete_array(PG_FUNCTION_ARGS)
                        int                     skeylen = HS_KEYLEN(es, i);
  
                        if (skeylen == key_pairs[j].keylen)
!                               difference = strncmp(HS_KEY(es, ps, i),
!                                                                        
key_pairs[j].key,
!                                                                        
key_pairs[j].keylen);
                        else
                                difference = (skeylen > key_pairs[j].keylen) ? 
1 : -1;
                }
--- 331,339 ----
                        int                     skeylen = HS_KEYLEN(es, i);
  
                        if (skeylen == key_pairs[j].keylen)
!                               difference = memcmp(HS_KEY(es, ps, i),
!                                                                       
key_pairs[j].key,
!                                                                       
key_pairs[j].keylen);
                        else
                                difference = (skeylen > key_pairs[j].keylen) ? 
1 : -1;
                }
***************
*** 416,424 **** hstore_delete_hstore(PG_FUNCTION_ARGS)
                        int                     s2keylen = HS_KEYLEN(es2, j);
  
                        if (skeylen == s2keylen)
!                               difference = strncmp(HS_KEY(es, ps, i),
!                                                                        
HS_KEY(es2, ps2, j),
!                                                                        
skeylen);
                        else
                                difference = (skeylen > s2keylen) ? 1 : -1;
                }
--- 416,424 ----
                        int                     s2keylen = HS_KEYLEN(es2, j);
  
                        if (skeylen == s2keylen)
!                               difference = memcmp(HS_KEY(es, ps, i),
!                                                                       
HS_KEY(es2, ps2, j),
!                                                                       
skeylen);
                        else
                                difference = (skeylen > s2keylen) ? 1 : -1;
                }
***************
*** 433,439 **** hstore_delete_hstore(PG_FUNCTION_ARGS)
                        if (snullval != HS_VALISNULL(es2, j)
                                || (!snullval
                                        && (svallen != HS_VALLEN(es2, j)
!                                               || strncmp(HS_VAL(es, ps, i), 
HS_VAL(es2, ps2, j), svallen) != 0)))
                        {
                                HS_COPYITEM(ed, bufd, pd,
                                                        HS_KEY(es, ps, i), 
HS_KEYLEN(es, i),
--- 433,439 ----
                        if (snullval != HS_VALISNULL(es2, j)
                                || (!snullval
                                        && (svallen != HS_VALLEN(es2, j)
!                                               || memcmp(HS_VAL(es, ps, i), 
HS_VAL(es2, ps2, j), svallen) != 0)))
                        {
                                HS_COPYITEM(ed, bufd, pd,
                                                        HS_KEY(es, ps, i), 
HS_KEYLEN(es, i),
***************
*** 526,534 **** hstore_concat(PG_FUNCTION_ARGS)
                        int                     s2keylen = HS_KEYLEN(es2, 
s2idx);
  
                        if (s1keylen == s2keylen)
!                               difference = strncmp(HS_KEY(es1, ps1, s1idx),
!                                                                        
HS_KEY(es2, ps2, s2idx),
!                                                                        
s1keylen);
                        else
                                difference = (s1keylen > s2keylen) ? 1 : -1;
                }
--- 526,534 ----
                        int                     s2keylen = HS_KEYLEN(es2, 
s2idx);
  
                        if (s1keylen == s2keylen)
!                               difference = memcmp(HS_KEY(es1, ps1, s1idx),
!                                                                       
HS_KEY(es2, ps2, s2idx),
!                                                                       
s1keylen);
                        else
                                difference = (s1keylen > s2keylen) ? 1 : -1;
                }
***************
*** 996,1002 **** hstore_contains(PG_FUNCTION_ARGS)
                        if (nullval != HS_VALISNULL(ve, idx)
                                || (!nullval
                                        && (vallen != HS_VALLEN(ve, idx)
!                       || strncmp(HS_VAL(te, tstr, i), HS_VAL(ve, vstr, idx), 
vallen))))
                                res = false;
                }
                else
--- 996,1002 ----
                        if (nullval != HS_VALISNULL(ve, idx)
                                || (!nullval
                                        && (vallen != HS_VALLEN(ve, idx)
!                       || memcmp(HS_VAL(te, tstr, i), HS_VAL(ve, vstr, idx), 
vallen))))
                                res = false;
                }
                else
*** a/contrib/ltree/lquery_op.c
--- b/contrib/ltree/lquery_op.c
***************
*** 111,117 **** checkLevel(lquery_level *curq, ltree_level *curt)
  
        for (i = 0; i < curq->numvar; i++)
        {
!               cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : 
strncmp;
  
                if (curvar->flag & LVAR_SUBLEXEME)
                {
--- 111,117 ----
  
        for (i = 0; i < curq->numvar; i++)
        {
!               cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : 
memcmp;
  
                if (curvar->flag & LVAR_SUBLEXEME)
                {
*** a/contrib/ltree/ltree_gist.c
--- b/contrib/ltree/ltree_gist.c
***************
*** 546,552 **** gist_tqcmp(ltree *t, lquery *q)
        while (an > 0 && bn > 0)
        {
                bl = LQL_FIRST(ql);
!               if ((res = strncmp(al->name, bl->name, Min(al->len, bl->len))) 
== 0)
                {
                        if (al->len != bl->len)
                                return al->len - bl->len;
--- 546,552 ----
        while (an > 0 && bn > 0)
        {
                bl = LQL_FIRST(ql);
!               if ((res = memcmp(al->name, bl->name, Min(al->len, bl->len))) 
== 0)
                {
                        if (al->len != bl->len)
                                return al->len - bl->len;
*** a/contrib/ltree/ltree_op.c
--- b/contrib/ltree/ltree_op.c
***************
*** 68,74 **** ltree_compare(const ltree *a, const ltree *b)
  
        while (an > 0 && bn > 0)
        {
!               if ((res = strncmp(al->name, bl->name, Min(al->len, bl->len))) 
== 0)
                {
                        if (al->len != bl->len)
                                return (al->len - bl->len) * 10 * (an + 1);
--- 68,74 ----
  
        while (an > 0 && bn > 0)
        {
!               if ((res = memcmp(al->name, bl->name, Min(al->len, bl->len))) 
== 0)
                {
                        if (al->len != bl->len)
                                return (al->len - bl->len) * 10 * (an + 1);
***************
*** 165,171 **** inner_isparent(const ltree *c, const ltree *p)
        {
                if (cl->len != pl->len)
                        return false;
!               if (strncmp(cl->name, pl->name, cl->len))
                        return false;
  
                pn--;
--- 165,171 ----
        {
                if (cl->len != pl->len)
                        return false;
!               if (memcmp(cl->name, pl->name, cl->len))
                        return false;
  
                pn--;
***************
*** 373,379 **** ltree_index(PG_FUNCTION_ARGS)
                        bptr = LTREE_FIRST(b);
                        for (j = 0; j < b->numlevel; j++)
                        {
!                               if (!(aptr->len == bptr->len && 
strncmp(aptr->name, bptr->name, aptr->len) == 0))
                                        break;
                                aptr = LEVEL_NEXT(aptr);
                                bptr = LEVEL_NEXT(bptr);
--- 373,379 ----
                        bptr = LTREE_FIRST(b);
                        for (j = 0; j < b->numlevel; j++)
                        {
!                               if (!(aptr->len == bptr->len && 
memcmp(aptr->name, bptr->name, aptr->len) == 0))
                                        break;
                                aptr = LEVEL_NEXT(aptr);
                                bptr = LEVEL_NEXT(bptr);
***************
*** 451,457 **** lca_inner(ltree **a, int len)
                        num = 0;
                        for (i = 0; i < Min(tmp, (*ptr)->numlevel - 1); i++)
                        {
!                               if (l1->len == l2->len && strncmp(l1->name, 
l2->name, l1->len) == 0)
                                        num = i + 1;
                                else
                                        break;
--- 451,457 ----
                        num = 0;
                        for (i = 0; i < Min(tmp, (*ptr)->numlevel - 1); i++)
                        {
!                               if (l1->len == l2->len && memcmp(l1->name, 
l2->name, l1->len) == 0)
                                        num = i + 1;
                                else
                                        break;
*** a/contrib/ltree/ltxtquery_op.c
--- b/contrib/ltree/ltxtquery_op.c
***************
*** 57,63 **** checkcondition_str(void *checkval, ITEM *val)
        char       *op = ((CHKVAL *) checkval)->operand + val->distance;
        int                     (*cmpptr) (const char *, const char *, size_t);
  
!       cmpptr = (val->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
        while (tlen > 0)
        {
                if (val->flag & LVAR_SUBLEXEME)
--- 57,63 ----
        char       *op = ((CHKVAL *) checkval)->operand + val->distance;
        int                     (*cmpptr) (const char *, const char *, size_t);
  
!       cmpptr = (val->flag & LVAR_INCASE) ? ltree_strncasecmp : memcmp;
        while (tlen > 0)
        {
                if (val->flag & LVAR_SUBLEXEME)
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1196,1202 **** parseNodeString(void)
        token = pg_strtok(&length);
  
  #define MATCH(tokname, namelen) \
!       (length == namelen && strncmp(token, tokname, namelen) == 0)
  
        if (MATCH("QUERY", 5))
                return_value = _readQuery();
--- 1196,1202 ----
        token = pg_strtok(&length);
  
  #define MATCH(tokname, namelen) \
!       (length == namelen && memcmp(token, tokname, namelen) == 0)
  
        if (MATCH("QUERY", 5))
                return_value = _readQuery();
*** a/src/backend/tsearch/spell.c
--- b/src/backend/tsearch/spell.c
***************
*** 1502,1508 **** CheckCompoundAffixes(CMPDAffix **ptr, char *word, int len, 
bool CheckInPlace)
        {
                while ((*ptr)->affix)
                {
!                       if (len > (*ptr)->len && strncmp((*ptr)->affix, word, 
(*ptr)->len) == 0)
                        {
                                len = (*ptr)->len;
                                issuffix = (*ptr)->issuffix;
--- 1502,1508 ----
        {
                while ((*ptr)->affix)
                {
!                       if (len > (*ptr)->len && memcmp((*ptr)->affix, word, 
(*ptr)->len) == 0)
                        {
                                len = (*ptr)->len;
                                issuffix = (*ptr)->issuffix;
*** a/src/backend/tsearch/to_tsany.c
--- b/src/backend/tsearch/to_tsany.c
***************
*** 90,96 **** uniqueWORD(ParsedWord *a, int4 l)
        while (ptr - a < l)
        {
                if (!(ptr->len == res->len &&
!                         strncmp(ptr->word, res->word, res->len) == 0))
                {
                        /*
                         * Got a new word, so put it in result
--- 90,96 ----
        while (ptr - a < l)
        {
                if (!(ptr->len == res->len &&
!                         memcmp(ptr->word, res->word, res->len) == 0))
                {
                        /*
                         * Got a new word, so put it in result
*** a/src/backend/tsearch/ts_selfuncs.c
--- b/src/backend/tsearch/ts_selfuncs.c
***************
*** 316,323 **** tsquery_opr_selec(QueryItem *item, char *operand,
                                int                     tlen = 
VARSIZE_ANY_EXHDR(t->element);
  
                                if (tlen >= key.length &&
!                                       strncmp(key.lexeme, 
VARDATA_ANY(t->element),
!                                                       key.length) == 0)
                                        matched += t->frequency;
                                allmcvs += t->frequency;
                        }
--- 316,323 ----
                                int                     tlen = 
VARSIZE_ANY_EXHDR(t->element);
  
                                if (tlen >= key.length &&
!                                       memcmp(key.lexeme, 
VARDATA_ANY(t->element),
!                                                  key.length) == 0)
                                        matched += t->frequency;
                                allmcvs += t->frequency;
                        }
***************
*** 430,434 **** compare_lexeme_textfreq(const void *e1, const void *e2)
                return -1;
  
        /* Fall back on byte-for-byte comparison */
!       return strncmp(key->lexeme, VARDATA_ANY(t->element), len1);
  }
--- 430,434 ----
                return -1;
  
        /* Fall back on byte-for-byte comparison */
!       return memcmp(key->lexeme, VARDATA_ANY(t->element), len1);
  }
*** a/src/backend/tsearch/ts_typanalyze.c
--- b/src/backend/tsearch/ts_typanalyze.c
***************
*** 488,494 **** lexeme_compare(const void *key1, const void *key2)
        else if (d1->length < d2->length)
                return -1;
        /* Lengths are equal, do a byte-by-byte comparison */
!       return strncmp(d1->lexeme, d2->lexeme, d1->length);
  }
  
  /*
--- 488,494 ----
        else if (d1->length < d2->length)
                return -1;
        /* Lengths are equal, do a byte-by-byte comparison */
!       return memcmp(d1->lexeme, d2->lexeme, d1->length);
  }
  
  /*
*** a/src/backend/utils/adt/varchar.c
--- b/src/backend/utils/adt/varchar.c
***************
*** 690,696 **** bpchareq(PG_FUNCTION_ARGS)
        if (len1 != len2)
                result = false;
        else
!               result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) 
== 0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
--- 690,696 ----
        if (len1 != len2)
                result = false;
        else
!               result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) == 
0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
***************
*** 717,723 **** bpcharne(PG_FUNCTION_ARGS)
        if (len1 != len2)
                result = true;
        else
!               result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) 
!= 0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
--- 717,723 ----
        if (len1 != len2)
                result = true;
        else
!               result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) != 
0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
***************
*** 905,911 **** internal_bpchar_pattern_compare(BpChar *arg1, BpChar *arg2)
        len1 = bcTruelen(arg1);
        len2 = bcTruelen(arg2);
  
!       result = strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
        if (result != 0)
                return result;
        else if (len1 < len2)
--- 905,911 ----
        len1 = bcTruelen(arg1);
        len2 = bcTruelen(arg2);
  
!       result = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
        if (result != 0)
                return result;
        else if (len1 < len2)
*** a/src/backend/utils/adt/varlena.c
--- b/src/backend/utils/adt/varlena.c
***************
*** 1286,1292 **** varstr_cmp(char *arg1, int len1, char *arg2, int len2)
         */
        if (lc_collate_is_c())
        {
!               result = strncmp(arg1, arg2, Min(len1, len2));
                if ((result == 0) && (len1 != len2))
                        result = (len1 < len2) ? -1 : 1;
        }
--- 1286,1292 ----
         */
        if (lc_collate_is_c())
        {
!               result = memcmp(arg1, arg2, Min(len1, len2));
                if ((result == 0) && (len1 != len2))
                        result = (len1 < len2) ? -1 : 1;
        }
***************
*** 1370,1376 **** varstr_cmp(char *arg1, int len1, char *arg2, int len2)
                         */
                        if (result == 0)
                        {
!                               result = strncmp(arg1, arg2, Min(len1, len2));
                                if ((result == 0) && (len1 != len2))
                                        result = (len1 < len2) ? -1 : 1;
                        }
--- 1370,1376 ----
                         */
                        if (result == 0)
                        {
!                               result = memcmp(arg1, arg2, Min(len1, len2));
                                if ((result == 0) && (len1 != len2))
                                        result = (len1 < len2) ? -1 : 1;
                        }
***************
*** 1462,1469 **** texteq(PG_FUNCTION_ARGS)
        if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
                result = false;
        else
!               result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
!                                                 VARSIZE_ANY_EXHDR(arg1)) == 
0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
--- 1462,1469 ----
        if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
                result = false;
        else
!               result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
!                                                VARSIZE_ANY_EXHDR(arg1)) == 0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
***************
*** 1485,1492 **** textne(PG_FUNCTION_ARGS)
        if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
                result = true;
        else
!               result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
!                                                 VARSIZE_ANY_EXHDR(arg1)) != 
0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
--- 1485,1492 ----
        if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
                result = true;
        else
!               result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
!                                                VARSIZE_ANY_EXHDR(arg1)) != 0);
  
        PG_FREE_IF_COPY(arg1, 0);
        PG_FREE_IF_COPY(arg2, 1);
***************
*** 1612,1618 **** internal_text_pattern_compare(text *arg1, text *arg2)
        len1 = VARSIZE_ANY_EXHDR(arg1);
        len2 = VARSIZE_ANY_EXHDR(arg2);
  
!       result = strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
        if (result != 0)
                return result;
        else if (len1 < len2)
--- 1612,1618 ----
        len1 = VARSIZE_ANY_EXHDR(arg1);
        len2 = VARSIZE_ANY_EXHDR(arg2);
  
!       result = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
        if (result != 0)
                return result;
        else if (len1 < len2)
\timing on

-- Benchmark "texteq".  Creates a table having a single "text" column and
-- inserts a small number of rows, each having the same length (~10 MiB; the
-- data does compress over 98%).  The datums differ only in the last few
-- characters.  Then, compare to another, similar string.

BEGIN;
CREATE TEMP TABLE t (c text);
INSERT INTO t
        SELECT repeat('foobarbazz', 1024 * 1024) || to_char(n, '000000')
        FROM generate_series(1,100) ser(n);
SELECT pg_size_pretty(pg_total_relation_size('t'));

CREATE FUNCTION pg_temp.try() RETURNS void LANGUAGE plpgsql AS $$
BEGIN
        FOR i IN 1..15 LOOP
                PERFORM count(*) FROM t WHERE c = repeat('foobarbazz', 1024 * 
1024) || to_char(0, '000000');
        END LOOP;
END
$$;
SELECT pg_temp.try();
SELECT pg_temp.try();
SELECT pg_temp.try();
SELECT pg_temp.try();
SELECT pg_temp.try();
ROLLBACK;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to