Конференция "Начинающим" » странное поведение Case оператора в консольном приложении
 
  • Гена © (24.12.16 19:48) [0]
    Компилятор D7.

    в большом цикле выполнятся оператор Case

    всегда выполняется за ~150сек

    Randomize;
    for i := 0 to 2147483647 do begin
     case Random(20) of
      1:;
      .....
      20:;
     end;
    end;



    а если сместить чуть метки, примерно так:

    Randomize;
    for i := 0 to 2147483647 do begin
     case Random(20,40) of
      20:;
      .....
      40:;
     end;
    end;



    то такой код (с другим диапазоном меток) начинает выполняться быстрее,
    всегда ~90 сек.

    Причем данный эффект наблюдатся только в консольном приложении, в оконном два примера работают одинаково.

    Может есть опции компилятора для разной трансляции case?
  • kilkennycat © (24.12.16 20:01) [1]

    >  Random(20,40)

    я вот уже плохо помню, но разве это не RandomRange? которая немного не Random(20)
  • Гена © (24.12.16 20:10) [2]

    > kilkennycat ©   (24.12.16 20:01) [1]

    Это условно, писалось в посте от руки
  • Гена © (24.12.16 20:13) [3]
    У меня только одна бредовая идея приходит на ум из этих результатов.

    Что когда диапазон маленький те от 1..20 поиск нужного ключа идет перебором, а когда значения поболее от 20..40, то поиск ключа идет бинарным поиском.
  • Inovet © (24.12.16 20:22) [4]
    Уверен, что распределение равномерное в 0-20 и в 20-40. А у тебя разное количество меток к тому же, т.е. в первом случае каждое 20 значение не попадает ни на одну метку.
  • Гена © (24.12.16 20:26) [5]

    > Уверен, что распределение равномерное в 0-20 и в 20-40

    на 100%, я просто нумерацию начал не с 0, а с 20, а в метках код остался прежним.
  • kilkennycat © (24.12.16 20:29) [6]

    > Гена ©   (24.12.16 20:10) [2]
    > Это условно, писалось в посте от руки

    ну круто, чё. меряешь время-то неусловно, вопрос задаешь неусловно, а что меряешь, какой код - условно.

    >  а когда значения поболее от 20..40,

    о возникает вопрос, как сделал от 20. если RandomRange, то это уже модуль Math

    а вообще, дизасм в помощь.
  • Гена © (24.12.16 20:55) [7]

    > а вообще, дизасм в помощь.

    Это б если я был умный а не учился на первом курсе.

    Вобщем, кому интересно напишите в чем же дело.

    Беру, просто перетосовую беспорядочно метки в Case т.е. не по порядку от 1..20,
    а 20,5,3,17 итд и результат выполнения становиться быстрей т.е те же ~90 сек второго примера, а не ~150 как если бы метки ишли по порядку.

    Получается компилятор генерирует разный код.
  • Гена © (24.12.16 21:02) [8]
    А и еще один прикол: догружаю case кучей пустых меток вида 100:; те варианты выбора которые несут нагрузку (как помните их 20 шт) разбрасываю среди этих вариантов и опять ~90, хотя казалось бы вариантов стало в 5 раз больше и работать должно в 5 раз медленнее, а работает наоборот быстрее.

    Наверно точно в одном случая компилятор принимает решение искать метки перебором, а когда их больше - бинарным поиском
  • Тимохов Дима © (24.12.16 22:15) [9]

    > Гена ©   (24.12.16 19:48) 


    С кейсом много чюдесов.
    Объяснить тебе смогут все деталях только те, кто разбирается в асме.
    Я, к сожалению, не разбираюсь...
  • Rouse_ © (25.12.16 00:07) [10]
    Смотря какой case, маленький джампами организован, большой через датамап, тут лучше асм листинг смотреть, у меня такого поведения не воспроизвелось
  • Гена © (25.12.16 00:18) [11]

    >  у меня такого поведения не воспроизвелось

    Так только в консоли, в оконных "не балуется"
  • Rouse_ © (25.12.16 01:49) [12]
    Экзешники архивом выложи, завтра или в пн гляну под IDA что там у тебя чудит, желательно с map файлами
  • NoUser © (25.12.16 01:54) [13]

    > Это б если я был умный а не учился на первом курсе.

    )))

    вот-вот, универы нынче зло!

    Ставишь точку останова (BreakPoint) , потом Ctrl+Alt+D, потом смотришь асм сам и нам показываешь.
  • Германн © (25.12.16 02:01) [14]

    > Гена ©   (25.12.16 00:18) [11]
    >
    >
    > >  у меня такого поведения не воспроизвелось
    >
    > Так только в консоли, в оконных "не балуется"
    >

    Не вижу разницы для компилятора в этих случаях.
    Разница может быть только в настройках конкретных проектов.
  • Rouse_ © (25.12.16 11:38) [15]

    > Германн ©   (25.12.16 02:01) [14]
    > Не вижу разницы для компилятора в этих случаях.
    > Разница может быть только в настройках конкретных проектов.

    Вот я бы небыл столь категоричен, знаешь сколько раз я уже натыкался на глюки компилятора? Вполне возможно что это один из таких вариантов.
    (Хотя, D7 - блин только обратил внимание, я то на берлине проверял)
  • kilkennycat © (25.12.16 14:10) [16]

    > глюки компилятора

    необязательно глюки. вот компиль xc8 иногда один и тот же код какой-то функции оптимизирует по-разному, только из-за того, что где-то совершенно далеко изменился размер переменной, которая к этой функции и отношения-то прямого не имеет. Мне так вместо простейшего сдвига такую хренотень замутил, что я не сразу-то и понял, че творится. При этом всё работает, конечно. Правда, не так :)
  • Тимохов Дима © (25.12.16 16:45) [17]

    > Rouse_ ©   (25.12.16 00:07) [10]
    > Смотря какой case, маленький джампами организован, большой
    > через датамап, тут лучше асм листинг смотреть, у меня такого
    > поведения не воспроизвелось


    Розыч, а у меня воспроизвелось. Только наоборот: у меня другие рандомы, правда (не думаю, что важно). Разница во времени процентов 20-30%. Чудеса.
  • Rouse_ © (25.12.16 20:13) [18]

    > Тимохов Дима ©   (25.12.16 16:45) [17]
    > Розыч, а у меня воспроизвелось. Только наоборот: у меня
    > другие рандомы, правда (не думаю, что важно). Разница во
    > времени процентов 20-30%. Чудеса.

    Ну скинь свой вариант, у меня оба варианта кода за 5 секунд (5.7) проходят.
    Я специально семерку с образа поднял и протестил именно в консоли.
  • Rouse_ © (25.12.16 20:24) [19]

    > kilkennycat ©   (25.12.16 14:10) [16]
    >  Мне так вместо простейшего сдвига такую хренотень замутил,
    >  что я не сразу-то и понял, че творится. При этом всё работает,
    >  конечно. Правда, не так :)

    Это скорее всего как раз прыжок через датамап (а вот с ним могут быть чудеса - на 2005 гарантированно воспроизводился один из вариантов, пока не пофиксили).
    На кодецентрале там даже большое обсуждение было.
  • Тимохов Дима © (25.12.16 21:47) [20]

    > Rouse_ ©   (25.12.16 20:13) [18]


    У меня дельфи2007.

    Саш, я загнул про 30%, там была ошибка теста(((
    Но разница все же есть.

    Убрал вообще Random. Дело не в нем.
    Test1 и Test2 - имеют одинаковый асм (смотрел CPU), только, есно, адреса переходов различаются.
    Но Test1 выполняется медленнее на от 5 до 18%. Причем, стабильно. Хоть сто раз запуская (без перекомпиляции или с ней - не важно), а вторая (по порядку в коде процедура) Test2 выполняется быстрее. Причем у меня и в оконном приложении похожая картина.

    Чудеса.

    О мастер анализа асма, поясни, пожалуйста, что за чудеса?


    program Project1;
    {$APPTYPE CONSOLE}
    {$O+}
    uses
     SysUtils, Windows;
    const
      cCount = 500000000;
    var
      r: Integer;
      f, b, a: TLargeInteger;
      d1, d2: Extended;
      s1, s2: String;

    procedure Test1;
    var i: Integer;
    begin
      QueryPerformanceCounter(b);
      for i := 0 to cCount do
         case i mod 2 of
            0: Inc(r);
            1: Dec(r);
         end;
      QueryPerformanceCounter(a);
      d1 := (a-b)/f;
      s1 := IntToStr(R)+'/'+FormatFloat('0.0000', d1);
    end;

    procedure Test2;
    var i: Integer;
    begin
      QueryPerformanceCounter(b);
      for i := 0 to cCount do
         case i mod 2 of
            0: Inc(r);
            1: Dec(r);
         end;
      QueryPerformanceCounter(a);
      d2 := (a-b)/f;
      s2 := IntToStr(R)+'/'+FormatFloat('0.0000', d2);
    end;

    begin
      QueryPerformanceFrequency(f);
      r := 0; Test2;
      r := 0; Test1;
      Writeln('Test1 = '+s1+'  | Test2 = '+s2+' | diff in % = '+FormatFloat('0.00', (d2-d1)/d1*100));
      Readln;
    end.

  • Rouse_ © (25.12.16 22:23) [21]
    Хм, это уже интересней, ок с утра гляну
  • Тимохов Дима © (25.12.16 22:57) [22]

    > Rouse_ ©   (25.12.16 22:23) [21]
    > Хм, это уже интересней, ок с утра гляну

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

    )))
  • DVM © (25.12.16 23:01) [23]

    > Тимохов Дима ©   (25.12.16 21:47) [20]

    Разница есть только в 32 бит приложении (5%), в 64 бит ее либо нет, либо она скачет в пределах +/- 1%
  • Игорь Шевченко © (25.12.16 23:07) [24]
    Вот же делать людям нечего
  • Тимохов Дима © (25.12.16 23:09) [25]

    > Игорь Шевченко ©   (25.12.16 23:07) [24]
    > Вот же делать людям нечего

    Почему? Эксперимент, гипотеза, подтверждение гипотезы. Вполне научный метод.

    Игорь, согласись (ну или не согласись), ведь факт интересный - как может один и тот же код (на уровне асма - ниже, т.е. до опкодов или что там - не опускаюсь, ибо не умею) дает разный результат?
  • DVM © (25.12.16 23:09) [26]

    > Тимохов Дима ©   (25.12.16 21:47) [20]
    >

    А вообще ты неправильно замеряешь время.
    QueryPerformanceFrequency() надо вызывать сразу после действия, а не до.
  • DVM © (25.12.16 23:13) [27]

    > Тимохов Дима ©   (25.12.16 21:47) [20]

    Все эти странности из-за того, что CPU поднимает частоту после (или во время) вызова первой функции и вторая отрабатывает на более высокой частоте, чем первая.
  • Тимохов Дима © (25.12.16 23:16) [28]

    > DVM ©   (25.12.16 23:09) [26]
    >
    > > Тимохов Дима ©   (25.12.16 21:47) [20]
    > >
    >
    > А вообще ты неправильно замеряешь время.
    > QueryPerformanceFrequency() надо вызывать сразу после действия,
    >  а не до.

    это я здесь в примере переставил, чтобы процедуры короче были.
    у меня и так разница есть - не такая устойчивая, но от 2 до 12% бывает.
  • Тимохов Дима © (25.12.16 23:17) [29]

    > DVM ©   (25.12.16 23:13) [27]
    >
    > > Тимохов Дима ©   (25.12.16 21:47) [20]
    >
    > Все эти странности из-за того, что CPU поднимает частоту
    > после (или во время) вызова первой функции и вторая отрабатывает
    > на более высокой частоте, чем первая.

    Век живи - век учись.
    Спасибо. Очень похоже на правду.
  • invis © (26.12.16 02:43) [30]
    Test1 и Test2 - имеют одинаковый асм (смотрел CPU), только, есно, адреса переходов различаются.

    Адреса имеют значение, в интернетах пишут (ссылаясь на мануалы Интел), что рекомендуется выравнивать адрес перехода на 16.
    Но Дельфи этого не делает, иногда случайно попадает, иногда нет. Отсюда и разница.
  • Rouse_ © (26.12.16 11:02) [31]
    Нет никаких странностей, ибо:

    program Project1;

    {$APPTYPE CONSOLE}
    {$O+}
    uses
    SysUtils, Windows;
    const
     cCount = 500000000;
    var
     r: Integer;
     f, b, a: TLargeInteger;
     d1, d2: Extended;
     s1, s2: String;

    procedure Test1;
    var i: Integer;
    begin
     QueryPerformanceCounter(b);
     for i := 0 to cCount do
        case i mod 2 of
           0: Inc(r);
           1: Dec(r);
        end;
     QueryPerformanceCounter(a);
     QueryPerformanceFrequency(f);
     d1 := (a-b)/f;
     s1 := IntToStr(R)+'/'+FormatFloat('0.0000', d1);
    end;

    procedure Test2;
    var i: Integer;
    begin
     QueryPerformanceCounter(b);
     for i := 0 to cCount do
        case i mod 2 of
           0: Inc(r);
           1: Dec(r);
        end;
     QueryPerformanceCounter(a);
     QueryPerformanceFrequency(f);
     d2 := (a-b)/f;
     s2 := IntToStr(R)+'/'+FormatFloat('0.0000', d2);
    end;

    begin
     r := 0; Test1;
     r := 0; Test2;
     Writeln('Test1 = '+s1+'  | Test2 = '+s2+' | diff in % = '+FormatFloat('0.00', (d2-d1)/d1*100));
     r := 0; Test2;
     r := 0; Test1;
     Writeln('Test1 = '+s1+'  | Test2 = '+s2+' | diff in % = '+FormatFloat('0.00', (d2-d1)/d1*100));
     Readln;
    end.


    Вот так все будет нормально с легкой девиацией.
  • DVM © (26.12.16 11:05) [32]

    > Rouse_ ©   (26.12.16 11:02) [31]

    Ну я ж говорю, он неправильно замерял время, у него из-за динамического изменения частоты у всех современных CPU время выполнения некорректно считалось.
  • Rouse_ © (26.12.16 11:35) [33]
    Ну да, я тоже не с первого раза заметил, поэтому и удивился.
  • Rouse_ © (26.12.16 11:36) [34]
    Кстати выяснилось что этот-же код на 64 битах работает в два раза медленней чем на 32-ух. Хотя асм выхлоп ровный без излишеств, а вот это уже печально.
  • Тимохов Дима © (26.12.16 11:48) [35]

    > Rouse_ ©   (26.12.16 11:35) [33]
    > Ну да, я тоже не с первого раза заметил, поэтому и удивился.
    >

    Да я в примере тут вынес QueryPerformanceFrequency, чтобы код короче был.
    Конечно, замеряю всегда QueryPerformanceFrequency после QueryPerformanceCounter (справку читал).

    Розыч, твой код - не сказал бы, чтобы легкая девиация была.
    Test1 = 1/1,3297  | Test2 = 1/1,4811 | diff in % = 11,39
    Test1 = 1/1,3866  | Test2 = 1/1,5992 | diff in % = 15,34



    Причем, картина стабильная.
  • Rouse_ © (26.12.16 11:57) [36]
    У меня вроде ровно:
    Test1 = 1/1,4481  | Test2 = 1/1,4482 | diff in % = 0,00
    Test1 = 1/1,4439  | Test2 = 1/1,4465 | diff in % = 0,18

  • Тимохов Дима © (26.12.16 12:02) [37]

    > Rouse_ ©   (26.12.16 11:57) [36]
    > У меня вроде ровно:


    В цикле прогнал: среднее значение 8%.

    Видать, особенности моего Intel Core 2 DUO.
    Или компилятор иной.

    Ладно) Забью - практического смысла ноль, видимо, Игорь прав.
  • invis © (26.12.16 14:42) [38]
    Попробуй поменять местами Test1 и Test2 (не вызовы, а сами функции).
  • NoUser © (28.12.16 18:27) [39]

    > [34]

    0041A5D6 81E201000080     and edx,$80000001


    vs
    0000000000427352 41F7F8           idiv r8d


    это без излишеств?


    > [35]
    > Причем, картина стабильная.

    Гипертрейдинг ?
 
Конференция "Начинающим" » странное поведение Case оператора в консольном приложении
Есть новые Нет новых   [134431   +10][b:0][p:0.002]