2015-10-24 2 views
-1

Я просматривал файл agents.h на моей ОС Windows, и мне захотелось увидеть код C++ без комментариев. Я разделил их, чтобы увидеть код более четко с моей старой программой, но я был удивлен, что это заняло всего 2 секунды для завершения. Размер файла составляет 605 КБ, так что это не так уж плохо. Почему это медленнее. Я подозреваю, что это функция ftell(), которая делает это, но я не могу сказать. Это ветвление что замедляется или ftell() ?, если ftell(), то каков лучший способ вернуть указатель FILE?Ветвление или ftell(), которое замедляется?

EDIT

#include <stdio.h> 
#include <time.h> 

#define NOT_COMMENT (!DOUBLESLASH_Comment && !ASTERISK_SLASH_Comment) 

int main(int argc,char *argv[]) 
{ 
    clock_t t1 = clock(); 

    FILE *input , *output; 

    if(fopen_s(&input,argv[1],"r")) 
    { 
     printf("error opening file %s\n",argv[1]); 
     return 0; 
    } 

    if(fopen_s(&output,argv[2],"w")) 
    { 
     printf("error opening file %s\n",argv[2]); 
     return 0; 
    } 

    char c , d; 
                   //escape flag 
    bool DOUBLESLASH_Comment = 0 , ASTERISK_SLASH_Comment = 0 , flag = 0; 

    /* single quotes/double quotes */ 
    int s_QUOTED = 0 , d_QUOTED = 0; 

    while((c=getc(input)) != EOF) 
    { 
     switch(c) 
     { 
     case '\\': 
      { 
       if(NOT_COMMENT) 
       { 
        if(flag == 1) 
         flag = 0; 
        else 
         flag = 1; 
       } 
      }break; 

     case '\'': 
      { 
       if(NOT_COMMENT && !d_QUOTED) 
       { 
        if(!flag) 
        { 
         s_QUOTED++; 
        } 
       } 
      }break; 

     case '"': 
      { 
       if(NOT_COMMENT && !flag) 
       { 
        if(!s_QUOTED) 
        { 
         d_QUOTED++; 
        } 
       } 
      }break; 

     case '/': 
      { 
       if(NOT_COMMENT && !d_QUOTED) 
       { 
        if((d=getc(input)) == '*') 
        { 
         ASTERISK_SLASH_Comment = 1; 
        } 
        else if(d == '/') 
        { 
         DOUBLESLASH_Comment = 1; 
        } 
        else 
        { 
         if(d != EOF) 
         { 
          ungetc(d,input); 
         } 
        } 
       } 
      }break; 

     case '*': 
      { 
       if(ASTERISK_SLASH_Comment) 
       { 
        if((d=getc(input)) == '/') 
        { 
         if((c=getc(input)) == EOF) 
          return 0; 

         ASTERISK_SLASH_Comment = 0; 
        } 
        else 
        { 
         if(d != EOF) 
         { 
          ungetc(d,input); 
         } 
        } 
       } 
      }break; 

     case '\n': 
      { 
       if(DOUBLESLASH_Comment) 
       { 
        DOUBLESLASH_Comment = 0; 
       } 
      }break; 
     } 

     if(NOT_COMMENT && c != '\\') flag = 0; 
     if(d_QUOTED == 2) d_QUOTED = 0; 
     if(s_QUOTED == 2) s_QUOTED = 0; 

     if(NOT_COMMENT) 
     { 
      fprintf(output,"%c",c); 
     } 
    } 

    fclose(input); 
    fclose(output); 

    clock_t t2 = clock(); 

    double elapsed = (double)(t2 - t1)/CLOCKS_PER_SEC; 

    printf("time elapsed : %f\n",elapsed); 
} 
+0

Не было бы значительно проще с линейным подходом вместо синтаксического разбора одного символа за раз с полной нагрузкой состояний? – usr2564301

+0

это программа, которую я написал давно. Я буду работать над ней позже, особенно, что хочу заменить несколько «if» на «switch», и я не закрыл escaping.but, похоже, что 'fseek() 'является бременем –

ответ

3

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

Что-то вроде этого:

char lookahead = ' '; 
bool havelookahead = false; 

char getNextChar(FILE *input) 
{ 
    if (havelookahead) 
    { 
     havelookahead = false; 
     return lookahead; 
    } 
    return getc(input); 
} 

char peekChar(FILE *input) 
{ 
    if (!havelookahead) 
    { 
     lookahead = getc(input); 
     havelookahead = true; 
    } 
    return lookahead; 
} 

Затем замените ваш getc с getNextChar в начале цикла, и где вы проверить следующий символ с peekChar (с последующим фиктивным getNextChar() потреблять его).

Это полезный шаблон в целом для синтаксического анализа - как на уровне персонажа, так и на уровне маркера, поэтому хорошо учиться понимать, как это работает.

Вы также можете использовать стандарт ungetc, чтобы «вернуть» своего персонажа, на который вы смотрели.

Является ли это тем, что ваш код работает значительно быстрее или нет, трудно сказать, как я сказал в начале.

+0

Благодарим вас за предложение и @Emanuele. Я рассмотрю его, и если это решит проблему, я вернусь, чтобы принять ответ. –

+0

Я оптимизировал эту версию, и это было в 24 раза быстрее с 'ungetc'. Но я не знал, как использовать ваши две функции. –

2

Я не могу скомпилировать код, так что я не могу сделать тесты. Но я подозреваю, что узким местом является fseek, а не ftell. Отказ от символа является общей задачей при анализе файлов ... и должен быть реализован библиотекой или некоторым промежуточным уровнем с некоторой буферизацией. В этом случае (отказ от одного символа) вы можете использовать ungetc для достижения этого.

Таким образом, вы должны заменить

fseek(file , (ftell(file) - 1) , SEEK_SET); 

с

ungetc('*', file); // ungetc('/', file); the second time. 
Смежные вопросы