Быстрая альтернатива sscanf - PullRequest
0 голосов
/ 09 ноября 2019

Я пишу парсер для текстового файла во встроенной системе на C. Мне нужно каждую секунду сотни раз использовать функцию sscanf, что создает проблемы с производительностью. Поэтому я пытаюсь найти быструю альтернативу sscanf.

Текстовый файл, который мне нужно проанализировать, содержит параметры вида «параметр =% d,% d,% d,% d». Имена параметров различаются по длине, но параметры всегда состоят из 4 целочисленных значений.

Анализатор читает одну строку текстового файла за другой и сохраняет строку в переменной «token». Переменная «format» содержит строки вида «параметр =% d,% d,% d,% d».

void Parser_GetQuadToken( char* token, const char* format, int16_t* res1, int16_t* res2, int16_t* res3, int16_t* res4 )
{
    uint32_t var1, var2, var3, var4;
    sscanf( token, format, &var1, &var2, &var3, &var4 ) );
    *res1 = var1;
    *res2 = var2;
    *res3 = var3;
    *res4 = var4;
}

У кого-нибудь есть идеи, как реализовать быструю альтернативу?

1 Ответ

2 голосов
/ 09 ноября 2019

sscanf() должен интерпретировать строку формата в соответствии с token и преобразовать содержимое строки. Вы можете избежать ненужного синтаксического анализа строки формата, используя функцию прямого преобразования, такую ​​как atoi():

#include <string.h>
#include <stdlib.h>

void Parser_GetQuadToken( const char* token, int16_t* res1, int16_t* res2, int16_t* res3, int16_t* res4 )
{
    char* resstr = strchr( token, '=' ) + 1 ;
    *res1 = (int16_t)atoi( resstr ) ;

    resstr = strchr( resstr, ',' ) + 1 ;
    *res2 = (int16_t)atoi( resstr ) ;

    resstr = strchr( resstr, ',' ) + 1 ;
    *res3 = (int16_t)atoi( resstr ) ;

    resstr = strchr( resstr, ',' ) + 1 ;
    *res4 = (int16_t)atoi( resstr ) ;
}

Поскольку целью является повышение производительности, необходимо измерить улучшение. Я измерил его в 9 раз быстрее в 64-битной отладочной сборке в VC ++ и в 12 раз быстрее для 32-битного кода. YMMV для вашей цели и компилятора. Оптимизация оказывает незначительное влияние, поскольку она связана с библиотечным кодом.

Обратите внимание, что в приведенной выше реализации столько же проверок на ошибки, сколько и в исходном коде, т. Е. Нет. Вы должны быть уверены, что ввод token будет действительным как предварительное условие. В моем тесте добавление проверки ошибок оказало незначительное влияние на производительность:

bool Parser_GetQuadToken( const char* token, int16_t* res1, int16_t* res2, int16_t* res3, int16_t* res4 )
{
    char* resstr = 0 ;
    if( (resstr = strchr( token, '=' )) != NULL )
    {
        *res1 = (int16_t)atoi( ++resstr ) ;

        if( (resstr = strchr( resstr, ',' )) != NULL )
        {
            *res2 = (int16_t)atoi( ++resstr ) ;

            if( (resstr = strchr( resstr, ',' )) != NULL )
            {
                *res3 = (int16_t)atoi( ++resstr ) ;

                if( (resstr = strchr( resstr, ',' )) != NULL )
                {
                    *res4 = (int16_t)atoi( ++resstr ) ;
                }
            }
        }
    }

    return resstr != NULL ;
}

Однако это может не оказать ожидаемого эффекта - файловый ввод / вывод будет намного медленнее, чем манипулирование памятью и строками, налагаемое sscanf() - проблема с sscanf() во встроенных системах чаще связана с требуемым использованием пространства кода и стека.

@ SteveSummit предложил использовать strtol(). Так как это отслеживает "последнюю позицию" отсканированного, чтобы избежать повторения строки. Вы можете использовать это, чтобы пропустить явный поиск разделителей запятых:

void Parser_GetQuadToken( const char* token, int16_t* res1, int16_t* res2, int16_t* res3, int16_t* res4 )
{
    char* resstr = strchr( token, '=' ) ;
    *res1 = (int16_t)strtol( ++resstr, &resstr, 10 ) ;
    *res2 = (int16_t)strtol( ++resstr, &resstr, 10 ) ;
    *res3 = (int16_t)strtol( ++resstr, &resstr, 10 ) ;
    *res4 = (int16_t)strtol( ++resstr, &resstr, 10 ) ;
}

В моих тестах это примерно в 12 раз быстрее при отладке и 17 при оптимизации - однако это несколько переменное тестирование в Windows. Я оставлю проверку ошибок на ваше усмотрение.

...