про сравнение строк
От: niXman Ниоткуда https://github.com/niXman
Дата: 22.11.16 13:45
Оценка: :)
занудамод.

этот фрагмент кода:
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 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.