[PATCH] Small optimization across postgres (remove strlen duplicate usage)
Hi,
strlen it is one of the low fruits that can be harvested.
What is your opinion?
regards,
Ranier Vilela
Attachments:
remove_strlen.patchapplication/octet-stream; name=remove_strlen.patchDownload
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 0ff9ca9f2c..bc67b20e0f 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -152,7 +152,7 @@ CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
int i;
/* Reduce empty-string to NULL case */
- if (comment != NULL && strlen(comment) == 0)
+ if (comment != NULL && comment[0] == '\0')
comment = NULL;
/* Prepare to form or update a tuple, if necessary */
@@ -247,7 +247,7 @@ CreateSharedComments(Oid oid, Oid classoid, const char *comment)
int i;
/* Reduce empty-string to NULL case */
- if (comment != NULL && strlen(comment) == 0)
+ if (comment != NULL && comment[0] == '\0')
comment = NULL;
/* Prepare to form or update a tuple, if necessary */
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 694114aded..331f4cde99 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -907,7 +907,7 @@ interpret_AS_clause(Oid languageOid, const char *languageName,
* modicum of backwards compatibility, accept an empty "prosrc"
* value as meaning the supplied SQL function name.
*/
- if (strlen(*prosrc_str_p) == 0)
+ if (**prosrc_str_p == '\0')
*prosrc_str_p = funcname;
}
}
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 2baca12c5f..0ba0a65da6 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -2369,11 +2369,12 @@ ChooseIndexColumnNames(List *indexElems)
foreach(lc, indexElems)
{
+ char buf[NAMEDATALEN];
IndexElem *ielem = (IndexElem *) lfirst(lc);
const char *origname;
const char *curname;
int i;
- char buf[NAMEDATALEN];
+ int origlen;
/* Get the preliminary name from the IndexElem */
if (ielem->indexcolname)
@@ -2384,6 +2385,7 @@ ChooseIndexColumnNames(List *indexElems)
origname = "expr"; /* default name for expression */
/* If it conflicts with any previous column, tweak it */
+ origlen = strlen(origname);
curname = origname;
for (i = 1;; i++)
{
@@ -2399,11 +2401,11 @@ ChooseIndexColumnNames(List *indexElems)
if (lc2 == NULL)
break; /* found nonconflicting name */
- sprintf(nbuf, "%d", i);
+ nlen = sprintf(nbuf, "%d", i);
/* Ensure generated names are shorter than NAMEDATALEN */
- nlen = pg_mbcliplen(origname, strlen(origname),
- NAMEDATALEN - 1 - strlen(nbuf));
+ nlen = pg_mbcliplen(origname, origlen,
+ NAMEDATALEN - 1 - nlen);
memcpy(buf, origname, nlen);
strcpy(buf + nlen, nbuf);
curname = buf;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 0578e92ba9..ade71ba00b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -571,6 +571,7 @@ PostmasterMain(int argc, char *argv[])
char *userDoption = NULL;
bool listen_addr_saved = false;
int i;
+ int len;
char *output_config_variable = NULL;
InitProcessGlobals();
@@ -759,8 +760,9 @@ PostmasterMain(int argc, char *argv[])
case 'o':
/* Other options to pass to the backend on the command line */
- snprintf(ExtraOptions + strlen(ExtraOptions),
- sizeof(ExtraOptions) - strlen(ExtraOptions),
+ len = strlen(ExtraOptions);
+ snprintf(ExtraOptions + len,
+ sizeof(ExtraOptions) - len,
" %s", optarg);
break;
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index e4fd1f9bb6..77f1d9a8f1 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -300,11 +300,11 @@ libpqrcv_get_senderinfo(WalReceiverConn *conn, char **sender_host,
Assert(conn->streamConn != NULL);
ret = PQhost(conn->streamConn);
- if (ret && strlen(ret) != 0)
+ if (ret && ret[0] != '\0')
*sender_host = pstrdup(ret);
ret = PQport(conn->streamConn);
- if (ret && strlen(ret) != 0)
+ if (ret && ret[0] != '\0')
*sender_port = atoi(ret);
}
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index abae74c9a5..97f62635eb 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -174,8 +174,10 @@ bool
ReplicationSlotValidateName(const char *name, int elevel)
{
const char *cp;
+ int len;
- if (strlen(name) == 0)
+ len = strlen(name);
+ if (len == 0)
{
ereport(elevel,
(errcode(ERRCODE_INVALID_NAME),
@@ -184,7 +186,7 @@ ReplicationSlotValidateName(const char *name, int elevel)
return false;
}
- if (strlen(name) >= NAMEDATALEN)
+ if (len >= NAMEDATALEN)
{
ereport(elevel,
(errcode(ERRCODE_NAME_TOO_LONG),
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 7dc6dd2f15..5f04b34ee1 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3077,7 +3077,7 @@ RemovePgTempFilesInDir(const char *tmpdirname, bool missing_ok, bool unlink_all)
if (unlink_all ||
strncmp(temp_de->d_name,
PG_TEMP_FILE_PREFIX,
- strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ sizeof(PG_TEMP_FILE_PREFIX) - 1) == 0)
{
struct stat statbuf;
@@ -3543,7 +3543,7 @@ fsync_parent_path(const char *fname, int elevel)
* just a file name (see comments in path.c), so handle that as being the
* current directory.
*/
- if (strlen(parentpath) == 0)
+ if (parentpath[0] == '\0')
strlcpy(parentpath, ".", MAXPGPATH);
if (fsync_fname_ext(parentpath, true, false, elevel) != 0)
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index ef64d08357..2238d59351 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -283,11 +283,10 @@ dsm_cleanup_for_mmap(void)
/* Scan the directory for something with a name of the correct format. */
dir = AllocateDir(PG_DYNSHMEM_DIR);
-
while ((dent = ReadDir(dir, PG_DYNSHMEM_DIR)) != NULL)
{
- if (strncmp(dent->d_name, PG_DYNSHMEM_MMAP_FILE_PREFIX,
- strlen(PG_DYNSHMEM_MMAP_FILE_PREFIX)) == 0)
+ if (strncmp(dent->d_name, PG_DYNSHMEM_MMAP_FILE_PREFIX,
+ (sizeof(PG_DYNSHMEM_MMAP_FILE_PREFIX) - 1)) == 0)
{
char buf[MAXPGPATH + sizeof(PG_DYNSHMEM_DIR)];
diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c
index 8aab96d3b0..897481f745 100644
--- a/src/backend/tsearch/spell.c
+++ b/src/backend/tsearch/spell.c
@@ -1225,35 +1225,35 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
}
if (STRNCMP(recoded, "COMPOUNDFLAG") == 0)
- addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDFLAG"),
+ addCompoundAffixFlagValue(Conf, recoded + sizeof("COMPOUNDFLAG") - 1,
FF_COMPOUNDFLAG);
else if (STRNCMP(recoded, "COMPOUNDBEGIN") == 0)
- addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDBEGIN"),
+ addCompoundAffixFlagValue(Conf, recoded + sizeof("COMPOUNDBEGIN") - 1,
FF_COMPOUNDBEGIN);
else if (STRNCMP(recoded, "COMPOUNDLAST") == 0)
- addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDLAST"),
+ addCompoundAffixFlagValue(Conf, recoded + sizeof("COMPOUNDLAST") - 1,
FF_COMPOUNDLAST);
/* COMPOUNDLAST and COMPOUNDEND are synonyms */
else if (STRNCMP(recoded, "COMPOUNDEND") == 0)
- addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDEND"),
+ addCompoundAffixFlagValue(Conf, recoded + sizeof("COMPOUNDEND") - 1,
FF_COMPOUNDLAST);
else if (STRNCMP(recoded, "COMPOUNDMIDDLE") == 0)
- addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDMIDDLE"),
+ addCompoundAffixFlagValue(Conf, recoded + sizeof("COMPOUNDMIDDLE") - 1,
FF_COMPOUNDMIDDLE);
else if (STRNCMP(recoded, "ONLYINCOMPOUND") == 0)
- addCompoundAffixFlagValue(Conf, recoded + strlen("ONLYINCOMPOUND"),
+ addCompoundAffixFlagValue(Conf, recoded + sizeof("ONLYINCOMPOUND") - 1,
FF_COMPOUNDONLY);
else if (STRNCMP(recoded, "COMPOUNDPERMITFLAG") == 0)
addCompoundAffixFlagValue(Conf,
- recoded + strlen("COMPOUNDPERMITFLAG"),
+ recoded + sizeof("COMPOUNDPERMITFLAG") - 1,
FF_COMPOUNDPERMITFLAG);
else if (STRNCMP(recoded, "COMPOUNDFORBIDFLAG") == 0)
addCompoundAffixFlagValue(Conf,
- recoded + strlen("COMPOUNDFORBIDFLAG"),
+ recoded + sizeof("COMPOUNDFORBIDFLAG") - 1,
FF_COMPOUNDFORBIDFLAG);
else if (STRNCMP(recoded, "FLAG") == 0)
{
- char *s = recoded + strlen("FLAG");
+ char *s = recoded + sizeof("FLAG") - 1;
while (*s && t_isspace(s))
s += pg_mblen(s);
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 4c299057a6..2c197958e3 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -2631,13 +2631,13 @@ xmldata_root_element_start(StringInfo result, const char *eltname,
if (top_level)
{
appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
- if (strlen(targetns) > 0)
+ if (targetns[0] != '\0')
appendStringInfo(result, " xmlns=\"%s\"", targetns);
}
if (xmlschema)
{
/* FIXME: better targets */
- if (strlen(targetns) > 0)
+ if (targetns[0] != '\0')
appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
else
appendStringInfoString(result, " xsi:noNamespaceSchemaLocation=\"#\"");
@@ -2900,7 +2900,7 @@ xsd_schema_element_start(StringInfo result, const char *targetns)
appendStringInfoString(result,
"<xsd:schema\n"
" xmlns:xsd=\"" NAMESPACE_XSD "\"");
- if (strlen(targetns) > 0)
+ if (targetns[0] != '\0')
appendStringInfo(result,
"\n"
" targetNamespace=\"%s\"\n"
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index 9dff1f5e82..1137c70370 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -566,8 +566,8 @@ substitute_libpath_macro(const char *name)
if ((sep_ptr = first_dir_separator(name)) == NULL)
sep_ptr = name + strlen(name);
- if (strlen("$libdir") != sep_ptr - name ||
- strncmp(name, "$libdir", strlen("$libdir")) != 0)
+ if ((sizeof("$libdir") - 1) != sep_ptr - name ||
+ strncmp(name, "$libdir", sizeof("$libdir") - 1) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid macro name in dynamic library path: %s",
@@ -594,7 +594,7 @@ find_in_dynamic_libpath(const char *basename)
AssertState(Dynamic_library_path != NULL);
p = Dynamic_library_path;
- if (strlen(p) == 0)
+ if (p[0] == '\0')
return NULL;
baselen = strlen(basename);
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 6fe25c023a..73fe12421e 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1177,8 +1177,9 @@ CreateLockFile(const char *filename, bool amPostmaster,
strlcat(buffer, "\n", sizeof(buffer));
errno = 0;
+ len = strlen(buffer);
pgstat_report_wait_start(WAIT_EVENT_LOCK_FILE_CREATE_WRITE);
- if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
+ if (write(fd, buffer, len) != len)
{
int save_errno = errno;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5bdc02fce2..a00cca2605 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10699,10 +10699,15 @@ GUCArrayDelete(ArrayType *array, const char *name)
struct config_generic *record;
ArrayType *newarray;
int i;
+ int len;
int index;
Assert(name);
+ /* if array is currently null, then surely nothing to delete */
+ if (!array)
+ return NULL;
+
/* test if the option is valid and we're allowed to set it */
(void) validate_option_array_item(name, NULL, false);
@@ -10711,12 +10716,9 @@ GUCArrayDelete(ArrayType *array, const char *name)
if (record)
name = record->name;
- /* if array is currently null, then surely nothing to delete */
- if (!array)
- return NULL;
-
newarray = NULL;
index = 1;
+ len = strlen(name);
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
@@ -10735,8 +10737,8 @@ GUCArrayDelete(ArrayType *array, const char *name)
val = TextDatumGetCString(d);
/* ignore entry if it's what we want to delete */
- if (strncmp(val, name, strlen(name)) == 0
- && val[strlen(name)] == '=')
+ if (strncmp(val, name, len) == 0
+ && val[len] == '=')
continue;
/* else add it to the output array */
diff --git a/src/common/exec.c b/src/common/exec.c
index f39b0a294b..fc360c2ef2 100644
--- a/src/common/exec.c
+++ b/src/common/exec.c
@@ -325,6 +325,7 @@ find_other_exec(const char *argv0, const char *target,
{
char cmd[MAXPGPATH];
char line[MAXPGPATH];
+ int len;
if (find_my_exec(argv0, retpath) < 0)
return -1;
@@ -334,7 +335,8 @@ find_other_exec(const char *argv0, const char *target,
canonicalize_path(retpath);
/* Now append the other program's name */
- snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
+ len = strlen(retpath);
+ snprintf(retpath + len, MAXPGPATH - len,
"/%s%s", target, EXE);
if (validate_exec(retpath) != 0)
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7584c1f2fb..193cb72c5a 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -320,7 +320,7 @@ fsync_parent_path(const char *fname)
* just a file name (see comments in path.c), so handle that as being the
* current directory.
*/
- if (strlen(parentpath) == 0)
+ if (parentpath[0] == '\0')
strlcpy(parentpath, ".", MAXPGPATH);
if (fsync_fname(parentpath, true) != 0)
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 66a50f183f..24bc469a4c 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -246,6 +246,7 @@ format_numeric_locale(const char *my_str)
char *new_str;
int new_len,
int_len,
+ len,
leading_digits,
i,
new_str_pos;
@@ -275,13 +276,14 @@ format_numeric_locale(const char *my_str)
}
/* process integer part of number */
+ len = strlen(thousands_sep);
for (i = 0; i < int_len; i++)
{
/* Time to insert separator? */
if (i > 0 && --leading_digits == 0)
{
strcpy(&new_str[new_str_pos], thousands_sep);
- new_str_pos += strlen(thousands_sep);
+ new_str_pos += len;
leading_digits = groupdigits;
}
new_str[new_str_pos++] = my_str[i];
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index c8869d5226..c41620da11 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -174,8 +174,8 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader;
* be complete yet.
*/
#define IsPartialXLogFileName(fname) \
- (strlen(fname) == XLOG_FNAME_LEN + strlen(".partial") && \
- strspn(fname, "0123456789ABCDEF") == XLOG_FNAME_LEN && \
+ (strlen(fname) == XLOG_FNAME_LEN + sizeof(".partial") - 1 && \
+ strspn(fname, "0123456789ABCDEF") == XLOG_FNAME_LEN && \
strcmp((fname) + XLOG_FNAME_LEN, ".partial") == 0)
#define XLogFromFileName(fname, tli, logSegNo, wal_segsz_bytes) \
@@ -195,8 +195,8 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader;
snprintf(fname, MAXFNAMELEN, "%08X.history", tli)
#define IsTLHistoryFileName(fname) \
- (strlen(fname) == 8 + strlen(".history") && \
- strspn(fname, "0123456789ABCDEF") == 8 && \
+ (strlen(fname) == 8 + sizeof(".history") - 1 && \
+ strspn(fname, "0123456789ABCDEF") == 8 && \
strcmp((fname) + 8, ".history") == 0)
#define TLHistoryFilePath(path, tli) \
@@ -214,7 +214,7 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader;
#define IsBackupHistoryFileName(fname) \
(strlen(fname) > XLOG_FNAME_LEN && \
strspn(fname, "0123456789ABCDEF") == XLOG_FNAME_LEN && \
- strcmp((fname) + strlen(fname) - strlen(".backup"), ".backup") == 0)
+ strcmp((fname) + strlen(fname) - sizeof(".backup") - 1, ".backup") == 0)
#define BackupHistoryFilePath(path, tli, logSegNo, startpoint, wal_segsz_bytes) \
snprintf(path, MAXPGPATH, XLOGDIR "/%08X%08X%08X.%08X.backup", tli, \
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index 69fcbdf6e2..d118a421f5 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -592,6 +592,7 @@ read_server_first_message(fe_scram_state *state, char *input)
char *encoded_salt;
char *nonce;
int decoded_salt_len;
+ int client_nonce_len;
state->server_first_message = strdup(input);
if (state->server_first_message == NULL)
@@ -611,8 +612,9 @@ read_server_first_message(fe_scram_state *state, char *input)
}
/* Verify immediately that the server used our part of the nonce */
- if (strlen(nonce) < strlen(state->client_nonce) ||
- memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
+ client_nonce_len = strlen(state->client_nonce);
+ if (strlen(nonce) < client_nonce_len ||
+ memcmp(nonce, state->client_nonce, client_nonce_len) != 0)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 0157c619aa..d385d4d0e1 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1705,7 +1705,7 @@ connectFailureMessage(PGconn *conn, int errorno)
* looked-up IP address.
*/
if (conn->connhost[conn->whichhost].type != CHT_HOST_ADDRESS &&
- strlen(host_addr) > 0 &&
+ host_addr[0] != '\0' &&
strcmp(displayed_host, host_addr) != 0)
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not connect to server: %s\n"
@@ -2529,7 +2529,7 @@ keep_going: /* We will come back to here until there is
}
getHostaddr(conn, host_addr, NI_MAXHOST);
- if (strlen(host_addr) > 0)
+ if (host_addr[0] != '\0')
conn->connip = strdup(host_addr);
/*
@@ -5164,8 +5164,9 @@ parseServiceFile(const char *serviceFile,
return 0;
}
- if (strncmp(line + 1, service, strlen(service)) == 0 &&
- line[strlen(service) + 1] == ']')
+ len = strlen(service);
+ if (strncmp(line + 1, service, len) == 0 &&
+ line[len + 1] == ']')
*group_found = true;
else
*group_found = false;
@@ -7129,7 +7130,7 @@ sslVerifyProtocolVersion(const char *version)
* An empty string and a NULL value are considered valid as it is
* equivalent to ignoring the parameter.
*/
- if (!version || strlen(version) == 0)
+ if (!version || version[0] == '\0')
return true;
if (pg_strcasecmp(version, "TLSv1") == 0 ||
@@ -7155,7 +7156,7 @@ sslVerifyProtocolRange(const char *min, const char *max)
sslVerifyProtocolVersion(max));
/* If at least one of the bounds is not set, the range is valid */
- if (min == NULL || max == NULL || strlen(min) == 0 || strlen(max) == 0)
+ if (min == NULL || max == NULL || min[0] == '\0' || max[0] == '\0')
return true;
/*
diff --git a/src/port/dirent.c b/src/port/dirent.c
index b264484fca..8292638a5c 100644
--- a/src/port/dirent.c
+++ b/src/port/dirent.c
@@ -34,6 +34,7 @@ opendir(const char *dirname)
{
DWORD attr;
DIR *d;
+ int len;
/* Make sure it is a directory */
attr = GetFileAttributes(dirname);
@@ -61,9 +62,9 @@ opendir(const char *dirname)
free(d);
return NULL;
}
- strcpy(d->dirname, dirname);
- if (d->dirname[strlen(d->dirname) - 1] != '/' &&
- d->dirname[strlen(d->dirname) - 1] != '\\')
+ len = sprintf(d->dirname, "%s", dirname);
+ if (d->dirname[len - 1] != '/' &&
+ d->dirname[len - 1] != '\\')
strcat(d->dirname, "\\"); /* Append backslash if not already there */
strcat(d->dirname, "*"); /* Search for entries named anything */
d->handle = INVALID_HANDLE_VALUE;
diff --git a/src/port/path.c b/src/port/path.c
index 10e87fbae8..0f457a5f85 100644
--- a/src/port/path.c
+++ b/src/port/path.c
@@ -232,8 +232,11 @@ join_path_components(char *ret_path,
if (*tail)
{
+ int len;
+
/* only separate with slash if head wasn't empty */
- snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
+ len = strlen(ret_path);
+ snprintf(ret_path + len, MAXPGPATH - len,
"%s%s",
(*(skip_drive(head)) != '\0') ? "/" : "",
tail);
On Sun, Apr 19, 2020 at 11:24:38AM -0300, Ranier Vilela wrote:
Hi,
strlen it is one of the low fruits that can be harvested.
What is your opinion?
That assumes this actually affects/improves performance, without any
measurements proving that. Considering large number of the places you
modified are related to DDL (CreateComment, ChooseIndexColumnNames, ...)
or stuff that runs only once or infrequently (like the changes in
PostmasterMain or libpqrcv_get_senderinfo). Likewise, it seems entirely
pointless to worry about strlen() overhead e.g. in fsync_parent_path
which is probably dominated by I/O.
Maybe there are places where this would help, but I don't see a reason
to just throw away all strlen calls and replace them with something
clearly less convenient and possibly more error-prone (I'd expect quite
a few off-by-one mistakes with this).
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Em dom., 19 de abr. de 2020 às 16:33, Tomas Vondra <
tomas.vondra@2ndquadrant.com> escreveu:
On Sun, Apr 19, 2020 at 11:24:38AM -0300, Ranier Vilela wrote:
Hi,
strlen it is one of the low fruits that can be harvested.
What is your opinion?That assumes this actually affects/improves performance, without any
measurements proving that. Considering large number of the places you
modified are related to DDL (CreateComment, ChooseIndexColumnNames, ...)
or stuff that runs only once or infrequently (like the changes in
PostmasterMain or libpqrcv_get_senderinfo). Likewise, it seems entirely
pointless to worry about strlen() overhead e.g. in fsync_parent_path
which is probably dominated by I/O.
With code as interconnected as postgres, it is difficult to say that a
function, which calls strlen, repeatedly, would not have any gain.
Regarding the functions, I was just being consistent, trying to remove all
occurrences, even where, there is very little gain.
Maybe there are places where this would help, but I don't see a reason
to just throw away all strlen calls and replace them with something
clearly less convenient and possibly more error-prone (I'd expect quite
a few off-by-one mistakes with this).
Yes, always, it is prone to errors, but for the most part, they are safe
changes.
It passes all 199 tests, of course it has not been tested in a real
production environment.
Perhaps the time is not the best, the end of the cycle, but, once done, I
believe it would be a good harvest.
Thank you for comment.
regards,
Ranier Vilela
On Sun, Apr 19, 2020 at 05:29:52PM -0300, Ranier Vilela wrote:
Em dom., 19 de abr. de 2020 �s 16:33, Tomas Vondra <
tomas.vondra@2ndquadrant.com> escreveu:On Sun, Apr 19, 2020 at 11:24:38AM -0300, Ranier Vilela wrote:
Hi,
strlen it is one of the low fruits that can be harvested.
What is your opinion?That assumes this actually affects/improves performance, without any
measurements proving that. Considering large number of the places you
modified are related to DDL (CreateComment, ChooseIndexColumnNames, ...)
or stuff that runs only once or infrequently (like the changes in
PostmasterMain or libpqrcv_get_senderinfo). Likewise, it seems entirely
pointless to worry about strlen() overhead e.g. in fsync_parent_path
which is probably dominated by I/O.With code as interconnected as postgres, it is difficult to say that a
function, which calls strlen, repeatedly, would not have any gain.
Regarding the functions, I was just being consistent, trying to remove all
occurrences, even where, there is very little gain.
That very much depends on the function, I think. For most places modified
by this patch it's not that hard, I think. The DDL cases (comments and
indexes) seem pretty clear. Similarly for the command parsing, wal
receiver, lockfile creation, guc, exec.c, and so on.
Perhaps the only places worth changing might be xml.c and spell.c, but
I'm not convinced even these are worth it, really.
Maybe there are places where this would help, but I don't see a reason
to just throw away all strlen calls and replace them with something
clearly less convenient and possibly more error-prone (I'd expect quite
a few off-by-one mistakes with this).Yes, always, it is prone to errors, but for the most part, they are safe
changes.
It passes all 199 tests, of course it has not been tested in a real
production environment.
Perhaps the time is not the best, the end of the cycle, but, once done, I
believe it would be a good harvest.
I wasn't really worried about bugs in this patch, but rather in future
changes made to this code. Off-by-one errors are trivial to make.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 4/19/20 10:29 PM, Ranier Vilela wrote:
Em dom., 19 de abr. de 2020 às 16:33, Tomas Vondra
<tomas.vondra@2ndquadrant.com <mailto:tomas.vondra@2ndquadrant.com>>
escreveu:On Sun, Apr 19, 2020 at 11:24:38AM -0300, Ranier Vilela wrote:
Hi,
strlen it is one of the low fruits that can be harvested.
What is your opinion?That assumes this actually affects/improves performance, without any
measurements proving that. Considering large number of the places you
modified are related to DDL (CreateComment, ChooseIndexColumnNames, ...)
or stuff that runs only once or infrequently (like the changes in
PostmasterMain or libpqrcv_get_senderinfo). Likewise, it seems entirely
pointless to worry about strlen() overhead e.g. in fsync_parent_path
which is probably dominated by I/O.With code as interconnected as postgres, it is difficult to say that a
function, which calls strlen, repeatedly, would not have any gain.
Regarding the functions, I was just being consistent, trying to remove
all occurrences, even where, there is very little gain.
At least gcc 9.3 optimizes "strlen(s) == 0" to "s[0] == '\0'", even at
low optimization levels. I tried it out with https://godbolt.org/.
Maybe some of the others cases are performance improvements, I have not
checked your patch in details, but strlen() == 0 is easily handled by
the compiler.
C code:
int f1(char *str) {
return strlen(str) == 0;
}
int f2(char *str) {
return str[0] == '\0';
}
Assembly generated with default flags:
f1:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movzbl (%rax), %eax
testb %al, %al
sete %al
movzbl %al, %eax
popq %rbp
ret
f2:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movzbl (%rax), %eax
testb %al, %al
sete %al
movzbl %al, %eax
popq %rbp
ret
Assembly generated with -O2.
f1:
xorl %eax, %eax
cmpb $0, (%rdi)
sete %al
ret
f2:
xorl %eax, %eax
cmpb $0, (%rdi)
sete %al
ret
Andreas
Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
On Sun, Apr 19, 2020 at 11:24:38AM -0300, Ranier Vilela wrote:
strlen it is one of the low fruits that can be harvested.
Maybe there are places where this would help, but I don't see a reason
to just throw away all strlen calls and replace them with something
clearly less convenient and possibly more error-prone (I'd expect quite
a few off-by-one mistakes with this).
I've heard it claimed that modern compilers will optimize
strlen('literal') to a constant at compile time. I'm not sure how
much I believe that, but to the extent that it's true, replacing such
calls would provide exactly no performance benefit.
I'm quite -1 on changing these to sizeof(), in any case, because
(a) that opens up room for confusion about whether the trailing nul is
included, and (b) it makes it very easy, when changing or copy/pasting
code, to apply sizeof to something that's not a string literal, with
disastrous results.
The cases where Ranier proposes to replace strlen(foo) == 0
with a test on foo[0] do seem like wins, though. Asking for
the full string length to be computed is more computation than
necessary, and it's less clear that the compiler could be
expected to save you from that. Anyway there's a coding style
proposition that we should be doing this consistently, and
certainly lots of places do do this without using strlen().
I can't get excited about the proposed changes to optimize away
multiple calls of strlen() either, unless there's performance
measurements to support them individually. This again seems like
something a compiler might do for you.
regards, tom lane
On Mon, 20 Apr 2020 at 09:38, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The cases where Ranier proposes to replace strlen(foo) == 0
with a test on foo[0] do seem like wins, though. Asking for
the full string length to be computed is more computation than
necessary, and it's less clear that the compiler could be
expected to save you from that. Anyway there's a coding style
proposition that we should be doing this consistently, and
certainly lots of places do do this without using strlen().
Looking at https://godbolt.org/z/6XsjbA it seems like GCC is pretty
good at getting rid of the strlen call even at -O0. It takes -O1 for
clang to use it and -O2 for icc.
David
Em dom., 19 de abr. de 2020 às 19:00, David Rowley <dgrowleyml@gmail.com>
escreveu:
On Mon, 20 Apr 2020 at 09:38, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The cases where Ranier proposes to replace strlen(foo) == 0
with a test on foo[0] do seem like wins, though. Asking for
the full string length to be computed is more computation than
necessary, and it's less clear that the compiler could be
expected to save you from that. Anyway there's a coding style
proposition that we should be doing this consistently, and
certainly lots of places do do this without using strlen().Looking at https://godbolt.org/z/6XsjbA it seems like GCC is pretty
good at getting rid of the strlen call even at -O0. It takes -O1 for
clang to use it and -O2 for icc.
I tried: https://godbolt.org with:
-O2:
f1:
int main (int argv, char **argc)
{
return strlen(argc[0]) == 0;
}
f1: Assembly
main: # @main
mov rcx, qword ptr [rsi]
xor eax, eax
cmp byte ptr [rcx], 0
sete al
ret
f2:
int main (int argv, char **argc)
{
return argc[0] == '\0';
}
f2: Assembly
main: # @main
xor eax, eax
cmp qword ptr [rsi], 0
sete al
ret
For me clearly str [0] == '\ 0', wins.
regards,
Ranier Vilela
Em dom., 19 de abr. de 2020 às 18:38, Tom Lane <tgl@sss.pgh.pa.us> escreveu:
Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
On Sun, Apr 19, 2020 at 11:24:38AM -0300, Ranier Vilela wrote:
strlen it is one of the low fruits that can be harvested.
Maybe there are places where this would help, but I don't see a reason
to just throw away all strlen calls and replace them with something
clearly less convenient and possibly more error-prone (I'd expect quite
a few off-by-one mistakes with this).I've heard it claimed that modern compilers will optimize
strlen('literal') to a constant at compile time. I'm not sure how
much I believe that, but to the extent that it's true, replacing such
calls would provide exactly no performance benefit.
Tom, I wouldn't believe it very much, they still have a lot of stupid
compilers out there (msvc for example).
Furthermore, optimizations are a complicated business, often the adjacent
code does not allow for such optimizations.
When a programmer does, there is no doubt.
I'm quite -1 on changing these to sizeof(), in any case, because
(a) that opens up room for confusion about whether the trailing nul is
included, and (b) it makes it very easy, when changing or copy/pasting
code, to apply sizeof to something that's not a string literal, with
disastrous results.
It may be true, but I have seen a lot of Postgres code, where sizeof is
used extensively, even with real chances of what you said happened. So that
risk already exists.
The cases where Ranier proposes to replace strlen(foo) == 0
with a test on foo[0] do seem like wins, though. Asking for
the full string length to be computed is more computation than
necessary, and it's less clear that the compiler could be
expected to save you from that. Anyway there's a coding style
proposition that we should be doing this consistently, and
certainly lots of places do do this without using strlen().
Yes, this is the idea.
I can't get excited about the proposed changes to optimize away
multiple calls of strlen() either, unless there's performance
measurements to support them individually. This again seems like
something a compiler might do for you.
Again, the compiler will not always save us.
I have seen many fanstatic solutions in Postgres, but I have also seen a
lot of written code, forgive me, without caprice, without much care.
The idea is, little by little, to prevent carefree code, either written or
left in Postgres.
You can see an example in that patch.
After a few calls the programmer validates the entry and leaves if it is
bad. When it should be done before anything.
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5bdc02fce2..a00cca2605 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10699,10 +10699,15 @@ GUCArrayDelete(ArrayType *array, const char *name)
struct config_generic *record;
ArrayType *newarray;
int i;
+ int len;
int index;
Assert(name);
+ /* if array is currently null, then surely nothing to delete */
+ if (!array)
+ return NULL;
+
/* test if the option is valid and we're allowed to set it */
(void) validate_option_array_item(name, NULL, false);
@@ -10711,12 +10716,9 @@ GUCArrayDelete(ArrayType *array, const char *name)
if (record)
name = record->name;
- /* if array is currently null, then surely nothing to delete */
- if (!array)
- return NULL;
-
newarray = NULL;
index = 1;
+ len = strlen(name);
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
@@ -10735,8 +10737,8 @@ GUCArrayDelete(ArrayType *array, const char *name)
val = TextDatumGetCString(d);
/* ignore entry if it's what we want to delete */
- if (strncmp(val, name, strlen(name)) == 0
- && val[strlen(name)] == '=')
+ if (strncmp(val, name, len) == 0
+ && val[len] == '=')
continue;
/* else add it to the output array */
regards,
Ranier Vilela
On Mon, 20 Apr 2020 at 11:24, Ranier Vilela <ranier.vf@gmail.com> wrote:
I tried: https://godbolt.org with:
-O2:
f1:
int main (int argv, char **argc)
{
return strlen(argc[0]) == 0;
}f1: Assembly
main: # @main
mov rcx, qword ptr [rsi]
xor eax, eax
cmp byte ptr [rcx], 0
sete al
retf2:
int main (int argv, char **argc)
{
return argc[0] == '\0';
}f2: Assembly
main: # @main
xor eax, eax
cmp qword ptr [rsi], 0
sete al
retFor me clearly str [0] == '\ 0', wins.
I think you'd want to use argc[0][0] == '\0' or *argc[0] == '\0'.
Otherwise you appear just to be checking if the first element in the
argc pointer array is set to NULL, which is certainly not the same as
an empty string.
David
Em dom., 19 de abr. de 2020 às 22:00, David Rowley <dgrowleyml@gmail.com>
escreveu:
On Mon, 20 Apr 2020 at 11:24, Ranier Vilela <ranier.vf@gmail.com> wrote:
I tried: https://godbolt.org with:
-O2:
f1:
int main (int argv, char **argc)
{
return strlen(argc[0]) == 0;
}f1: Assembly
main: # @main
mov rcx, qword ptr [rsi]
xor eax, eax
cmp byte ptr [rcx], 0
sete al
retf2:
int main (int argv, char **argc)
{
return argc[0] == '\0';
}f2: Assembly
main: # @main
xor eax, eax
cmp qword ptr [rsi], 0
sete al
retFor me clearly str [0] == '\ 0', wins.
I think you'd want to use argc[0][0] == '\0' or *argc[0] == '\0'.
Otherwise you appear just to be checking if the first element in the
argc pointer array is set to NULL, which is certainly not the same as
an empty string.
I guess you're right.
x86-64 clang (trunk) -O2
f1:
int cmp(const char * name)
{
return strlen(name) == 0;
}
cmp: # @cmp
xor eax, eax
cmp byte ptr [rdi], 0
sete al
ret
f2:
int cmp(const char * name)
{
return name[0] == '\0';
}
cmp: # @cmp
xor eax, eax
cmp byte ptr [rdi], 0
sete al
ret
Is the same result in assembly.
Well, it doesn't matter to me, I will continue to use str[0] == '\0'.
Thanks for take part.
regards,
Ranier VIlela