Re: [HACKERS] Binary search in fmgr_isbuiltin() is a bottleneck. - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: [HACKERS] Binary search in fmgr_isbuiltin() is a bottleneck. |
Date | |
Msg-id | 12940.1506625185@sss.pgh.pa.us Whole thread Raw |
In response to | Re: [HACKERS] Binary search in fmgr_isbuiltin() is a bottleneck. (Tom Lane <tgl@sss.pgh.pa.us>) |
List | pgsql-hackers |
I wrote: > [ we should use an index array ] Just to prove the point, I threw together the attached trivial test case, which time-trials the existing fmgr_isbuiltin implementation against both the proposed hash implementation and a simple index array. On my machine, with a repeat count of 10000, I get NOTICE: bsearch runtime 4234.087 ms NOTICE: hash runtime 2542.636 ms NOTICE: index runtime 165.184 ms (These numbers are repeatable within 1% or so.) It could be argued that trialling OIDs sequentially gives a bit of an unfair advantage to the bsearch and index methods over the hash method, because the former are going to suffer fewer cache misses that way. But I don't see a randomized lookup order changing the conclusion much. regards, tom lane #include "postgres.h" #include "fmgr.h" #include "access/transam.h" #include "portability/instr_time.h" #include "utils/fmgrtab.h" #include "utils/hashutils.h" PG_MODULE_MAGIC; static const FmgrBuiltin * fmgr_isbuiltin_bsearch(Oid id) { int low = 0; int high = fmgr_nbuiltins - 1; /* * Loop invariant: low is the first index that could contain target entry, * and high is the last index that could contain it. */ while (low <= high) { int i = (high + low) / 2; const FmgrBuiltin *ptr = &fmgr_builtins[i]; if (id == ptr->foid) return ptr; else if (id > ptr->foid) low = i + 1; else high = i - 1; } return NULL; } /* * Hashtable for fast lookup builtin functions. */ typedef struct BuiltinOidLookupEntry { Oid foid; int status; const FmgrBuiltin *builtin; } BuiltinOidLookupEntry; /* define hashtable mapping block numbers to PagetableEntry's */ #define SH_PREFIX oid2builtins #define SH_ELEMENT_TYPE BuiltinOidLookupEntry #define SH_KEY_TYPE Oid #define SH_KEY foid #define SH_HASH_KEY(tb, key) murmurhash32(key) #define SH_EQUAL(tb, a, b) a == b #define SH_SCOPE static inline #define SH_DEFINE #define SH_DECLARE #include "lib/simplehash.h" static oid2builtins_hash * oid2builtins = 0; static const FmgrBuiltin * fmgr_isbuiltin_hash(Oid id) { BuiltinOidLookupEntry *entry; entry = oid2builtins_lookup(oid2builtins, id); if (entry) return entry->builtin; else return NULL; } static void hash_setup(void) { int i; oid2builtins = oid2builtins_create(TopMemoryContext, fmgr_nbuiltins, NULL); for (i = 0; i < fmgr_nbuiltins; i++) { const FmgrBuiltin *ptr = &fmgr_builtins[i]; BuiltinOidLookupEntry *entry; bool found; entry = oid2builtins_insert(oid2builtins, ptr->foid, &found); Assert(!found); entry->builtin = ptr; } } static int16 builtins_index[FirstBootstrapObjectId]; static const FmgrBuiltin * fmgr_isbuiltin_index(Oid id) { int i; if (id >= FirstBootstrapObjectId) return NULL; i = builtins_index[id]; if (i < 0) return NULL; return &fmgr_builtins[i]; } static void index_setup(void) { int i; memset(builtins_index, -1, sizeof(builtins_index)); for (i = 0; i < fmgr_nbuiltins; i++) { const FmgrBuiltin *ptr = &fmgr_builtins[i]; builtins_index[ptr->foid] = i; } } PG_FUNCTION_INFO_V1(test_lookups); Datum test_lookups(PG_FUNCTION_ARGS) { int rep_count = PG_GETARG_INT32(0); instr_time start_time; instr_time end_time; int i; INSTR_TIME_SET_CURRENT(start_time); for (i = 0; i < rep_count; i++) { int ct = 0; Oid fo; for (fo = 1; fo < 10000; fo++) { if (fmgr_isbuiltin_bsearch(fo)) ct++; } if (ct != fmgr_nbuiltins) elog(ERROR, "got %d builtins instead of %d", ct, fmgr_nbuiltins); } INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); elog(NOTICE, "bsearch runtime %.3f ms", 1000.0 * INSTR_TIME_GET_DOUBLE(end_time)); hash_setup(); INSTR_TIME_SET_CURRENT(start_time); for (i = 0; i < rep_count; i++) { int ct = 0; Oid fo; for (fo = 1; fo < 10000; fo++) { if (fmgr_isbuiltin_hash(fo)) ct++; } if (ct != fmgr_nbuiltins) elog(ERROR, "got %d builtins instead of %d", ct, fmgr_nbuiltins); } INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); elog(NOTICE, "hash runtime %.3f ms", 1000.0 * INSTR_TIME_GET_DOUBLE(end_time)); index_setup(); INSTR_TIME_SET_CURRENT(start_time); for (i = 0; i < rep_count; i++) { int ct = 0; Oid fo; for (fo = 1; fo < 10000; fo++) { if (fmgr_isbuiltin_index(fo)) ct++; } if (ct != fmgr_nbuiltins) elog(ERROR, "got %d builtins instead of %d", ct, fmgr_nbuiltins); } INSTR_TIME_SET_CURRENT(end_time); INSTR_TIME_SUBTRACT(end_time, start_time); elog(NOTICE, "index runtime %.3f ms", 1000.0 * INSTR_TIME_GET_DOUBLE(end_time)); PG_RETURN_VOID(); } -- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers
pgsql-hackers by date: