Блог пользователя MikeMirzayanov

Автор MikeMirzayanov2 года назад, По-русски
Если вы пишите на С++, то регулярно бывает так, что ввод написанный через std::cin начинает тормозить из-за громоздких входных данных. Конечно правильнее в таких случаях сразу же писать чтение данных более эффективно - хотя бы с помощью функции scanf. Но если тестирующая система использует GNU C++ (проверял на MinGW 4.4.1, но думаю и на других версиях тоже будет работать), а переписывать программу не хочется, то можно катастрофически ускорить чтение всего одной строкой, размещенной в начале программы:  ios_base::sync_with_stdio( 0) .

На моем примере, где надо было найти сумму миллиона целых чисел, это ускорило программу в 4.5 раза, приблизив время работы к варианту со scanf. Пробовал запускать этот же тест на MS Visual C++ 9.0 - но не ускоряет.
 
 
 
 

 
2 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Это только для ввода или вывод тоже ускоряет?
 
2 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Затестил, риально работает!
также виден прирост скорости операций I/O на GNU C++3.4.2
 
2 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
наверное общеизвестный факт:
Если вы пишите на Java, то регулярно бывает так, что ввод написанный через Scanner начинает тормозить из-за громоздких входных данных. 
По старой привычке я автоматически набираю Scanner in = new Scanner (System.in); int n = in.nextInt(); Но по опыту уже знаю, что намного лучше обрабатывает входные данные StreamTokenizer, но строчка:
in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
 int nextInt() throws IOException
   {
      in.nextToken();
      return (int)in.nval;
   }
выглядит немного пугающе...

  •  
    2 года назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Я тоже пишу на Java. Как раз для таких случаев у меня припасён переписанный Сканер (Извините, если отступы сбились)

    class Scanner{

    StreamTokenizer in;

    Scanner(InputStream stream){
    in = new StreamTokenizer(new InputStreamReader(stream));
    }

    void asserT(boolean e){
    if (!e){
    throw new Error();
    }
    }

    int nextInt(){
    try{
    in.nextToken();
    asserT(in.ttype == in.TT_NUMBER);
    asserT(in.nval == (int)in.nval);
    return (int) in.nval;
    }catch (Exception e) {
    throw new Error();
    }
    }
    }

    Ну и метод запуска программы

    void run(){
    in = new Scanner(System.in);
    out = new PrintWriter(System.out);
    try {
    solve();
    }finally{
    out.close();
    }
    }

    Никаких посторонних "шаманств" не надо. Ассерты установлены, чтобы программа не дай бог не получила WA или ещё что-нить при некорректном вводе вместо Crash.

    •  
      2 года назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Извините за косяк в конструкторе ;)

      Scanner(InputStream stream) {
      in = new StreamTokenizer(new BufferedReader(new InputStreamReader(stream)));
      }

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

      •  
        2 года назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        На том же тимусе этот сканер часто спасал, когда количество чисел зашкаливало за 100 000. При меньших объемах выигрыш не значителен.
        •  
          2 года назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Огромное спасибо) сегодня же вложу в руки...
    •  
      6 месяцев назад, # ^ |
        Проголосовать: нравится -9 Проголосовать: не нравится
      Я один вместо "Assert" прочитал "Accept"? =)
 
6 месяцев назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Может есть способ сделать ускорение потокового ввод-вывода в Visual Studio?
 
6 месяцев назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
У меня не получилось можете пожалуйста написать простой пример кода?
  •  
    6 месяцев назад, # ^ |
    Правка 2   Проголосовать: нравится 0 Проголосовать: не нравится
    Во-первых: Пробовал запускать этот же тест на MS Visual C++ 9.0 - но не ускоряет.

    а вообще так по идее
    #include <iostream>
    using namespace std;
    int main()
    {
    ios_base::sync_with_stdio( 0);
    int n;
    for (;;)
    cin >> n;
    return 0;
    }

 
6 месяцев назад, # |
  Проголосовать: нравится -1 Проголосовать: не нравится
tooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooold
 
»
3 месяца назад, # |
  Проголосовать: нравится +1 Проголосовать: не нравится

Сейчас при решении кодчифа ( http://www.codechef.com/MARCH12/problems/HDELIVER ) столкнулся с одной проблемой.. раньше не встречал.. Кто-нибудь в курсе почему происходит такая вещь?

Значится так: я всегда пишу код в своём шаблоне, где у меня есть также строка ios::sync_with_stdio(0); в самом начале main(). Я обычно считываю cin/cout и программа не захотела заходить по ТЛ… ну естественно в первую очередь написал scanf(), но как бы зачем это делать в тех местах, которые отработают раз 100… так что исправил только там, где много данных считывается. Засылаю, получаю рантайм (ошибка доступа). Вобщем выяснил, что что-то не так считывается. Задача зашла только когда либо закомментил ios::sync_with_stdio(0); либо когда все cin исправил на scanf(). То есть проблема именно в том, что не может быть написано одновременно ios::sync_with_stdio(0); и чередование в середине программы cin и scanf(). Причём мне почему-то кажется, что раньше я так делал и никаких проблем связанных с этим не возникало.

  •  
    »
    »
    3 месяца назад, # ^ |
      Проголосовать: нравится +11 Проголосовать: не нравится

    Ну тут чётко написано: If the synchronization is turned off, the C++ standard streams are allowed to act independently. In this case using both C and C++ standard streams at the same time leads to undefined behavior.

  •  
    »
    »
    3 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Кстати возможно, что до этого всё было норм, ибо я отправлял в MSVS (а в ней работает всё корректно), а тут gcc-4.3.2.

    •  
      »
      »
      »
      3 месяца назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Мелкософт обычно плевать хотели на все стандарты, а тем более на какую-то там ios_base::sync_with_stdio — видно они не заморачиваются и синхронизируют любой i/o, игнорируя Ваши флаги.

      •  
        »
        »
        »
        »
        3 месяца назад, # ^ |
          Проголосовать: нравится -6 Проголосовать: не нравится

        Зачем так категорично :) Просто реализация от Microsoft зачастую в случае undefined behavior молча выбирает какой-нибудь из вариантов дальнейшего выполнения программы. Это вполне соответствует требованиям стандарта. Другое дело, что часто такое боком выходит.

        •  
          »
          »
          »
          »
          »
          3 месяца назад, # ^ |
          Правка 2   Проголосовать: нравится +3 Проголосовать: не нравится

          Значение флага ios_base::sync_with_stdio — это не undefined behavior, это вполне осмысленное поведение, которое ожидает программист, выставив этот флаг. Но Microsoft этот флаг просто игнорируют (им лень было это реализовывать).

          •  
            »
            »
            »
            »
            »
            »
            3 месяца назад, # ^ |
            Правка 2   Проголосовать: нравится +5 Проголосовать: не нравится

            Я вел речь об одновременном использовании sync_with_stdio(false) и чередовании cin/scanf, когда говорил про UB. А о том, что в реализации от Dinkumware (это ведь, кстати, не Microsoft виноват :) ) этот флаг игнорируется, я не знал. Вот что написано у них на сайте: “In this implementation there is no need to call the iostream member function sync_with_stdio to ensure such synchronization, nor is there any performance benefit in disabling it”. И дело тут, похоже, не в лени..

            •  
              »
              »
              »
              »
              »
              »
              »
              3 месяца назад, # ^ |
                Проголосовать: нравится -2 Проголосовать: не нравится

              UB при чередовании cin/scanf — есть очевидное следствие отсутствия синхронизации, что в свою очередь дает прирост производительности. То, что в реализации, используемой Microsoft, не реализован такой режим — вовсе не есть плюс.

              •  
                »
                »
                »
                »
                »
                »
                »
                »
                3 месяца назад, # ^ |
                  Проголосовать: нравится +5 Проголосовать: не нравится

                Я нигде и не говорил, что это плюс. Просто делать утверждения, что во всем виновата Microsoft, лень, etc, не подтверждая это фактами — легко. На деле же, обычно, причины каких-то “косяков” гораздо глубже.

                •  
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  3 месяца назад, # ^ |
                  Правка 2   Проголосовать: нравится -2 Проголосовать: не нравится

                  Хм… то, что было предусмотрено проектировщиками языка, но не было реализовано — как это назвать? Если не лень, то чёткий экономический просчёт — зачем тратить деньги на какую-то там оптимизацию, если это и так можно продать?

                  Главное же как себя подать — можно вон гордо написать “In this implementation there is no need to call the iostream member function sync_with_stdio to ensure such synchronization”, хотя слова “In this implementation” явно лишние — в стандарте чётко прописано поведение по-умолчанию.

                  Ну а оправдание типа “наш продукт такой гавёный не по нашей вине, а потому что мы покупаем для него такой-то кусок в другой фирме” звучит смешно. ;)

                •  
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  3 месяца назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится

                  А есть бенчмарки, которые показывают сильно лучшую скорость у gcc при выключенном синке? Если это не так, то я бы назвал это плюсом библиотеки от MS

                •  
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  3 месяца назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится

                  без синка — TL с синком — заходит за половину отведенного времени. Не редкость.

                •  
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  3 месяца назад, # ^ |
                  Правка 2   Проголосовать: нравится 0 Проголосовать: не нравится

                  Я не говорю о разнице gcc с синком и без, я говорю о разнице gcc с выключенным синком и MSVS

                •  
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  3 месяца назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится

                  Egor, пожалуйста:

                  1255624 — G++ c включенным синком (200 мс)

                  1255626 — G++ с выключенным синком (50 мс)

                  1255627 — MS C++ (230 мс)

  •  
    »
    »
    3 месяца назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

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

    Вам очень везло. :-) У меня как и ожидается из названия и описания метода даже следующая простая программа загибается на gcc 4.3 mingw:

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    int main() {
    	ios_base::sync_with_stdio(0);
    	int x, y;
    	cin >> x;
    	scanf("%d", &y);
    	cout << x << ' ' << y << endl;
    	return 0;
    }
    
  •  
    »
    »
    3 месяца назад, # ^ |
      Проголосовать: нравится +11 Проголосовать: не нравится

    А название функции ios::sync_with_stdio не намекает? :)

    •  
      »
      »
      »
      3 месяца назад, # ^ |
        Проголосовать: нравится -12 Проголосовать: не нравится

      :-D Ну да… даже не вдумывался, в одно время тупо запомнил и всё :)