занудамод.
этот фрагмент кода:
static Level convertFromString(const char* levelStr) {
if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0))
return Level::Global;
if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0))
return Level::Debug;
if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0))
return Level::Info;
if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0))
return Level::Warning;
if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0))
return Level::Error;
if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0))
return Level::Fatal;
if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0))
return Level::Verbose;
if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0))
return Level::Trace;
return Level::Unknown;
}
транслируется в
этот ассембелрный код(примеры для GCC и Intel).
Intel в данном случае поступил разуменее.
но, как мы видим,
strcmp() никто не зовет.
теперь давайте изменим этот код так, чтоб вместо
strcmp() использовалась
memcmp():
#define mymemcmp(l, r) memcmp(l, r, sizeof(r)-1)
Level convertFromString(const char* levelStr) {
if ((mymemcmp(levelStr, "GLOBAL") == 0) || (mymemcmp(levelStr, "global") == 0))
return Level::Global;
if ((mymemcmp(levelStr, "DEBUG") == 0) || (mymemcmp(levelStr, "debug") == 0))
return Level::Debug;
if ((mymemcmp(levelStr, "INFO") == 0) || (mymemcmp(levelStr, "info") == 0))
return Level::Info;
if ((mymemcmp(levelStr, "WARNING") == 0) || (mymemcmp(levelStr, "warning") == 0))
return Level::Warning;
if ((mymemcmp(levelStr, "ERROR") == 0) || (mymemcmp(levelStr, "error") == 0))
return Level::Error;
if ((mymemcmp(levelStr, "FATAL") == 0) || (mymemcmp(levelStr, "fatal") == 0))
return Level::Fatal;
if ((mymemcmp(levelStr, "VERBOSE") == 0) || (mymemcmp(levelStr, "verbose") == 0))
return Level::Verbose;
if ((mymemcmp(levelStr, "TRACE") == 0) || (mymemcmp(levelStr, "trace") == 0))
return Level::Trace;
return Level::Unknown;
}
результат.
GCC сейчас уже сравнивает строки так, буд-то бы это интегральные типы. т.е. в псевдокоде типа этого:
int v0 = *(int64*)"GLOBAL", v1 = *(int64*)levelStr; if (v0==v1) ...
Intel же снова умничает.
в некоторых
if`ах он использует
memcmp(), а в некоторых поступает как GCC. похоже, зависит от длины константной строки...
корректность использования
memcmp() для константных длин — отдельный вопрос, но можем сделать и так:
#define mymemcmp(l, s, r) s == sizeof(r)-1 && 0 == memcmp(l, r, s)
Level convertFromString(const char* levelStr) {
std::size_t s = strlen(levelStr);
if ((mymemcmp(levelStr, s, "GLOBAL")) || (mymemcmp(levelStr, s, "global")))
return Level::Global;
if ((mymemcmp(levelStr, s, "DEBUG")) || (mymemcmp(levelStr, s, "debug")))
return Level::Debug;
if ((mymemcmp(levelStr, s, "INFO")) || (mymemcmp(levelStr, s, "info")))
return Level::Info;
if ((mymemcmp(levelStr, s, "WARNING")) || (mymemcmp(levelStr, s, "warning")))
return Level::Warning;
if ((mymemcmp(levelStr, s, "ERROR")) || (mymemcmp(levelStr, s, "error")))
return Level::Error;
if ((mymemcmp(levelStr, s, "FATAL")) || (mymemcmp(levelStr, s, "fatal")))
return Level::Fatal;
if ((mymemcmp(levelStr, s, "VERBOSE")) || (mymemcmp(levelStr, s, "verbose")))
return Level::Verbose;
if ((mymemcmp(levelStr, s, "TRACE")) || (mymemcmp(levelStr, s, "trace")))
return Level::Trace;
return Level::Unknown;
}
результат.
вопрос эффективности я рассматривать не буду из-за недостаточной компетенции в ассемблерных вопросах, и из-за лени.
к этим тестам я пришел после прочтения кода этого проекта. меня всегда удивляли подобные конструкции, ветвления на основе сравнения строк, иногда множество лишних сравнений пока не найдется нужная ветка.
зы
я бы решил это как-то так:
Level convertFromString(const char* levelStr) {
if ( !levelStr ) return Level::Unknown;
switch (*levelStr) {
case 'G': case 'g': return Level::Global;
case 'D': case 'd': return Level::Debug;
case 'I': case 'i': return Level::Info;
case 'W': case 'w': return Level::Warning;
case 'E': case 'e': return Level::Error;
case 'F': case 'f': return Level::Fatal;
case 'V': case 'v': return Level::Verbose;
case 'T': case 't': return Level::Trace;
default: return Level::Unknown;
}
}
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)