Помощь новичкам

Скажите, a в чём особенность реализации loop, если:

begin
  var b:real;
  var d:real;

  Milliseconds;
  for var i:=1 to 1000 do b:=b+1;
  Println(Milliseconds); // три миллисекунды

  loop 1000 do d:=d+1;
  Println(Milliseconds); // двадцать миллисекунд
end.

Спасибо, Александр: при 10000000 получается правдоподобно 40мс : 56мс.

А как исправить такое:

uses System.Windows.Forms;

begin
 
begin
 var data:= new DataObject();
 data.SetData(DataFormats.UnicodeText, true, 'какой-то текст');
// Program4.pas(5) : Ошибка времени выполнения: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.

 var thread:=new Thread(()=>Clipboard.SetDataObject(data, true));
// Встречено '=', а ожидалось ':'

 thread.SetApartmentState(ApartmentState.STA);
 thread.Start();
 thread.Join();
end.

В неправильном использовании Milliseconds.

begin
  var b:real;
  var d:real;

  Milliseconds;
  for var i:=1 to 1000 do b:=b+1;
  Println(MillisecondsDelta);

  loop 1000 do d:=d+1;
  Println(MillisecondsDelta);
end.

Для корректных измерений нужно указать в циклах константу порядка хотя бы 10000000, а лучше большею Поскольку 16 мс - это длительность одного отсчета (“тика”), т.е. погрешность.

И снова в один тик разница. Увеличьте еще раз в десять. Если Вас реально интересует разница, числа должны быть четырехзначными

  1. Milliseconds это баловство, надо использовать System.Diagnostics.Stopwatch, у него на много больше точность (в >6к раз) и он умеет использовать аппаратную поддержку замеров времени (которая даёт ещё больше точности, до в 100к раз больше чем у Milliseconds).
begin
  var rc1 := 10000;
  var rc2 := 1000000;
  
  var b:real := 0;
  var d:real := 0;
  
  var sw1 := new System.Diagnostics.Stopwatch;
  var sw2 := new System.Diagnostics.Stopwatch;
  
  for var n := 1 to rc1 do // чтоб обнулить рандомные эффекты, влияющие на производительность - надо равномерно распределить их по всем тестам
  begin
    
    sw1.Start;
    loop rc2 do b += 1;
    sw1.Stop;
    
    sw2.Start;
    for var i := 1 to rc2 do d += 1;
    sw2.Stop;
    
    System.Console.Title := $'{n/rc1:P}'; // чтоб не скушно было ждать, так можно вытерпеть на много больший тест, раз так в 100
  end;
  
  writeln(sw1.Elapsed);
  writeln(sw2.Elapsed);
  readln;
end.

При чём, надо обязательно отключить отладку и запускать по Shift+F9, иначе результат может очень сильно отличатся. + желательно проверять время от времени, ибо паскаль любит включать отладку снова (обычно, если запустить без Shift+F9).

Результаты у меня:

00:00:18.3940394
00:00:20.6404646

Далее, тесты это прикольно и полезно, но тут ведь всё прозрачнее некуда, можно посмотреть напрямую какой код генерирует for, а какой loop. Я для этого использую DotPeek. Вот вся последняя программа:

    public static void $Main()
    {
      int num1 = 10000;
      int num2 = 1000000;
      double num3 = 0.0;
      double num4 = 0.0;
      Stopwatch stopwatch1 = new Stopwatch();
      Stopwatch stopwatch2 = new Stopwatch();
      int num5 = num1;
      int num6 = 1;
      if (num6 <= num5)
      {
        while (true)
        {
          stopwatch1.Start();
          int num7 = num2;
          int num8 = 1;
          if (num8 <= num7)
          {
            while (true)
            {
              ++num3;
              if (num8 < num7)
                ++num8;
              else
                break;
            }
          }
          stopwatch1.Stop();
          stopwatch2.Start();
          int num9 = num2;
          int num10 = 1;
          if (num10 <= num9)
          {
            while (true)
            {
              ++num4;
              if (num10 < num9)
                ++num10;
              else
                break;
            }
          }
          stopwatch2.Stop();
          Console.Title = string.Format("{0:P}", (object) ((double) num6 / (double) num1));
          if (num6 < num5)
            ++num6;
          else
            break;
        }
      }
      PABCSystem.PABCSystem.Writeln((object) stopwatch1.Elapsed);
      PABCSystem.PABCSystem.Writeln((object) stopwatch2.Elapsed);
      PABCSystem.PABCSystem.Readln();
    }

И её IL код:

  .method public static void 
    $Main() cil managed 
  {
    .maxstack 8
    .locals init (
      [0] int32 num1,
      [1] int32 num2,
      [2] float64 num3,
      [3] float64 num4,
      [4] class [System]System.Diagnostics.Stopwatch stopwatch1,
      [5] class [System]System.Diagnostics.Stopwatch stopwatch2,
      [6] int32 num6,
      [7] int32 num5,
      [8] int32 num8,
      [9] int32 num7,
      [10] int32 num10,
      [11] int32 num9
    )

    // [34 7 - 34 23]
    IL_0000: ldc.i4       10000 // 0x00002710
    IL_0005: stloc.0      // num1

    // [35 7 - 35 25]
    IL_0006: ldc.i4       1000000 // 0x000f4240
    IL_000b: stloc.1      // num2

    // [36 7 - 36 24]
    IL_000c: ldc.r8       0.0
    IL_0015: stloc.2      // num3

    // [37 7 - 37 24]
    IL_0016: ldc.r8       0.0
    IL_001f: stloc.3      // num4

    // [38 7 - 38 45]
    IL_0020: newobj       instance void [System]System.Diagnostics.Stopwatch::.ctor()
    IL_0025: stloc.s      stopwatch1

    // [39 7 - 39 45]
    IL_0027: newobj       instance void [System]System.Diagnostics.Stopwatch::.ctor()
    IL_002c: stloc.s      stopwatch2
    IL_002e: ldc.i4.1     
    IL_002f: stloc.s      V_6

    // [40 7 - 40 22]
    IL_0031: ldloc.0      // num1
    IL_0032: stloc.s      num5

    // [41 7 - 41 19]
    IL_0034: ldc.i4.1     
    IL_0035: stloc.s      num6

    // [42 7 - 42 24]
    IL_0037: ldloc.s      num6
    IL_0039: ldloc.s      num5
    IL_003b: cgt          
    IL_003d: ldc.i4.0     
    IL_003e: ceq          
    IL_0040: brfalse      IL_0108
    // start of loop, entry point: IL_0045

      // [46 11 - 46 29]
      IL_0045: ldloc.s      stopwatch1
      IL_0047: callvirt     instance void [System]System.Diagnostics.Stopwatch::Start()
      IL_004c: nop          
      IL_004d: ldc.i4.1     
      IL_004e: stloc.s      V_8

      // [47 11 - 47 26]
      IL_0050: ldloc.1      // num2
      IL_0051: stloc.s      num7

      // [48 11 - 48 23]
      IL_0053: ldc.i4.1     
      IL_0054: stloc.s      num8

      // [49 11 - 49 28]
      IL_0056: ldloc.s      num8
      IL_0058: ldloc.s      num7
      IL_005a: cgt          
      IL_005c: ldc.i4.0     
      IL_005d: ceq          
      IL_005f: brfalse      IL_0086
      // start of loop, entry point: IL_0064

        // [53 15 - 53 21]
        IL_0064: ldloc.2      // num3
        IL_0065: ldc.r8       1
        IL_006e: add          
        IL_006f: stloc.2      // num3

        // [54 15 - 54 31]
        IL_0070: ldloc.s      num8
        IL_0072: ldloc.s      num7
        IL_0074: clt          
        IL_0076: brfalse      IL_0086

        // [55 17 - 55 23]
        IL_007b: ldloc.s      num8
        IL_007d: ldc.i4.1     
        IL_007e: add          
        IL_007f: stloc.s      num8

        IL_0081: br           IL_0064
      // end of loop

      // [60 11 - 60 28]
      IL_0086: ldloc.s      stopwatch1
      IL_0088: callvirt     instance void [System]System.Diagnostics.Stopwatch::Stop()
      IL_008d: nop          

      // [61 11 - 61 29]
      IL_008e: ldloc.s      stopwatch2
      IL_0090: callvirt     instance void [System]System.Diagnostics.Stopwatch::Start()
      IL_0095: nop          
      IL_0096: ldc.i4.1     
      IL_0097: stloc.s      V_10

      // [62 11 - 62 26]
      IL_0099: ldloc.1      // num2
      IL_009a: stloc.s      num9

      // [63 11 - 63 24]
      IL_009c: ldc.i4.1     
      IL_009d: stloc.s      num10

      // [64 11 - 64 29]
      IL_009f: ldloc.s      num10
      IL_00a1: ldloc.s      num9
      IL_00a3: cgt          
      IL_00a5: ldc.i4.0     
      IL_00a6: ceq          
      IL_00a8: brfalse      IL_00cf
      // start of loop, entry point: IL_00ad

        // [68 15 - 68 21]
        IL_00ad: ldloc.3      // num4
        IL_00ae: ldc.r8       1
        IL_00b7: add          
        IL_00b8: stloc.3      // num4

        // [69 15 - 69 32]
        IL_00b9: ldloc.s      num10
        IL_00bb: ldloc.s      num9
        IL_00bd: clt          
        IL_00bf: brfalse      IL_00cf

        // [70 17 - 70 24]
        IL_00c4: ldloc.s      num10
        IL_00c6: ldc.i4.1     
        IL_00c7: add          
        IL_00c8: stloc.s      num10

        IL_00ca: br           IL_00ad
      // end of loop

      // [75 11 - 75 28]
      IL_00cf: ldloc.s      stopwatch2
      IL_00d1: callvirt     instance void [System]System.Diagnostics.Stopwatch::Stop()
      IL_00d6: nop          

      // [76 11 - 76 91]
      IL_00d7: ldstr        "{0:P}"
      IL_00dc: ldloc.s      num6
      IL_00de: conv.r8      
      IL_00df: ldloc.0      // num1
      IL_00e0: conv.r8      
      IL_00e1: div          
      IL_00e2: box          [mscorlib]System.Double
      IL_00e7: call         string [mscorlib]System.String::Format(string, object)
      IL_00ec: call         void [mscorlib]System.Console::set_Title(string)
      IL_00f1: nop          

      // [77 11 - 77 27]
      IL_00f2: ldloc.s      num6
      IL_00f4: ldloc.s      num5
      IL_00f6: clt          
      IL_00f8: brfalse      IL_0108

      // [78 13 - 78 19]
      IL_00fd: ldloc.s      num6
      IL_00ff: ldc.i4.1     
      IL_0100: add          
      IL_0101: stloc.s      num6

      IL_0103: br           IL_0045
    // end of loop

    // [83 7 - 83 65]
    IL_0108: ldloc.s      stopwatch1
    IL_010a: callvirt     instance valuetype [mscorlib]System.TimeSpan [System]System.Diagnostics.Stopwatch::get_Elapsed()
    IL_010f: box          [mscorlib]System.TimeSpan
    IL_0114: call         void PABCSystem.PABCSystem::Writeln(object)
    IL_0119: nop          

    // [84 7 - 84 65]
    IL_011a: ldloc.s      stopwatch2
    IL_011c: callvirt     instance valuetype [mscorlib]System.TimeSpan [System]System.Diagnostics.Stopwatch::get_Elapsed()
    IL_0121: box          [mscorlib]System.TimeSpan
    IL_0126: call         void PABCSystem.PABCSystem::Writeln(object)
    IL_012b: nop          

    // [85 7 - 85 37]
    IL_012c: call         void PABCSystem.PABCSystem::Readln()
    IL_0131: nop          
    IL_0132: ret          

  } // end of method Program::$Main

Теперь отдельно код loop:

          int num7 = num2;
          int num8 = 1;
          if (num8 <= num7)
          {
            while (true)
            {
              ++num3;
              if (num8 < num7)
                ++num8;
              else
                break;
            }
          }
    // [40 7 - 40 22]
    IL_0031: ldloc.0      // num1
    IL_0032: stloc.s      num5

    // [41 7 - 41 19]
    IL_0034: ldc.i4.1     
    IL_0035: stloc.s      num6

    // [42 7 - 42 24]
    IL_0037: ldloc.s      num6
    IL_0039: ldloc.s      num5
    IL_003b: cgt          
    IL_003d: ldc.i4.0     
    IL_003e: ceq          
    IL_0040: brfalse      IL_0108
    // start of loop, entry point: IL_0045

      // [46 11 - 46 29]
      IL_0045: ldloc.s      stopwatch1
      IL_0047: callvirt     instance void [System]System.Diagnostics.Stopwatch::Start()
      IL_004c: nop          
      IL_004d: ldc.i4.1     
      IL_004e: stloc.s      V_8

      // [47 11 - 47 26]
      IL_0050: ldloc.1      // num2
      IL_0051: stloc.s      num7

      // [48 11 - 48 23]
      IL_0053: ldc.i4.1     
      IL_0054: stloc.s      num8

      // [49 11 - 49 28]
      IL_0056: ldloc.s      num8
      IL_0058: ldloc.s      num7
      IL_005a: cgt          
      IL_005c: ldc.i4.0     
      IL_005d: ceq          
      IL_005f: brfalse      IL_0086
      // start of loop, entry point: IL_0064

        // [53 15 - 53 21]
        IL_0064: ldloc.2      // num3
        IL_0065: ldc.r8       1
        IL_006e: add          
        IL_006f: stloc.2      // num3

        // [54 15 - 54 31]
        IL_0070: ldloc.s      num8
        IL_0072: ldloc.s      num7
        IL_0074: clt          
        IL_0076: brfalse      IL_0086

        // [55 17 - 55 23]
        IL_007b: ldloc.s      num8
        IL_007d: ldc.i4.1     
        IL_007e: add          
        IL_007f: stloc.s      num8

        IL_0081: br           IL_0064
      // end of loop

И for:

          int num9 = num2;
          int num10 = 1;
          if (num10 <= num9)
          {
            while (true)
            {
              ++num4;
              if (num10 < num9)
                ++num10;
              else
                break;
            }
          }
      // [62 11 - 62 26]
      IL_0099: ldloc.1      // num2
      IL_009a: stloc.s      num9

      // [63 11 - 63 24]
      IL_009c: ldc.i4.1     
      IL_009d: stloc.s      num10

      // [64 11 - 64 29]
      IL_009f: ldloc.s      num10
      IL_00a1: ldloc.s      num9
      IL_00a3: cgt          
      IL_00a5: ldc.i4.0     
      IL_00a6: ceq          
      IL_00a8: brfalse      IL_00cf
      // start of loop, entry point: IL_00ad

        // [68 15 - 68 21]
        IL_00ad: ldloc.3      // num4
        IL_00ae: ldc.r8       1
        IL_00b7: add          
        IL_00b8: stloc.3      // num4

        // [69 15 - 69 32]
        IL_00b9: ldloc.s      num10
        IL_00bb: ldloc.s      num9
        IL_00bd: clt          
        IL_00bf: brfalse      IL_00cf

        // [70 17 - 70 24]
        IL_00c4: ldloc.s      num10
        IL_00c6: ldc.i4.1     
        IL_00c7: add          
        IL_00c8: stloc.s      num10

        IL_00ca: br           IL_00ad
      // end of loop

Я внимателно рассмотрел 2 кода и провёл ещё тестов… И я не понял откуда те 2 сек разницы. IL коды вроде одинаковые. А те 2 сек как то рандомно перемещаются между loop и for (если их менять местами целиком или по частям). Но это всё же не случайность, их даёт стабильно. В общем передаю эстафету вам, братья форумчане))

  1. Почитайте что такое STA и MTA потоки в .Net и с чем их едят (или скорее что ими закусывают).

  2. {$apptype windows} делает основной поток STA и одновременно отключает консоль. Включить назад можно с WinAPI AllocConsole (которое вызывается автоматически 1 раз при первом вызове write).

Может я что то путаю, но вы ведь вроде уже использовали лямбды? Если нет - читайте сначала это:
http://pascalabc.net/downloads/Presentations/Tutorials/ProcFuncLambdas.pdf

Спасибо, Сергей. Как-то не подумал, что объявление типа программы создаёт или решает столько тонкостей.

С лямбдами немного работал, но сходу не понял, что, в отличие от оригинала на шарпе, конструкция => в Pascal Abc . Net ничего не значит, только -> , a после упомянутой директивы надобность отпала.

Приложению с формами необходимо иметь STA поток в качестве основного, иначе большинство элементов управления не будут работать стабильно. Поэтому {$apptype windows} переключает это. Хотя конечно это не очень хорошо что нельзя отдельно контролировать тип основного потока…

1 лайк

loop синтаксическим сахаром заменяется на while. Можно глянуть код - он очень простой

Посмотрел несколько примеров Graph3d, который основан на WPF (который на DirectX) и не могу понять: ладно скорость, но если в отличие от WindowForms (GDI+) двойная буферизация реализована на аппаратном уровне, то как может мигать прорисовка?

Конкретно где она мигает?

Ибо если вы в 1 кадре удалили объект, а нарисовали на новой позиции только в следующем кадре - разумеется он мигнёт. Засунуть палку в колесо можно всегда.

P.S.

Передний и задний буфер существуют у каждой формы, когда она создаётся с соответствующими параметрами (и можно после запуска поменять). А ещё есть левые и правые буферы (то есть всего 4, левый передний и т.д.), если вы рисуете не на экране, а, к примеру, в шлеме VR.

И хранить эти буферы в памяти CPU, вместо GPU - было бы… Мягко говоря глупо. Поэтому, по сути - аппаратная поддержка двойной буферизации есть в любом графическом программном интерфейсе, у которого двойная буферизация вообще есть.

Сергей, как в Graph3d, так и GraphABC почти во всех примерах анимация выполнена через рисовка+задержка+удаление, что никак не синхронизировано.

Вы знаете как правильно или хотя бы как уменьшить мерцание? Подсказки в сети ведут к OpenGL, а ведь достаточно DX.

А как можно удалить в Graph3D ???

Где там такой пример?

Мерцания в WPF не должно быть в принципе.

  1. Как вам уже сказали разработчики - в Graph3D и нету LockDrawing, а значит и рисовка и рисовка происходит по другому принципу.

  2. То что в GraphABC перерисовка идёт через LockDrawing - не значит что нормальной двойной буферизации нет в GDI. Это не единственное что в GraphABC реализовано как попало.

    Однако! “рисовка+задержка+удаление” не применимо и в GraphABC. GraphABC тоже использует что то типа двойной буферизации, когда включено LockDrawing. Вот только вместо замены буферов, используется копирование заднего буфера в передний. Ну и задний буфер хранится в битмапе.

  1. OpenGL и DirectX это одинаковые по уровню библиотеки. Только в DirectX больше наполнения (сразу и для физики есть функции и т.п.). Но используя WPF - вы всё равно не сможете использовать ни OpenGL, ни DirectX в том же элементе управления (который может быть только целым окном в паскалевских модулях), так что те библиотеки тут не при чём.

  2. Да приведите вы наконец пример где это мерцание есть! При правильном использовании WPF мерцание таки невозможно.

Доброе утро всем. Подскажите пожалуйста, есть ли функция проверки на пустоту элемента управления TextBox или строкового типа.

Для строк есть функция string.IsNullOrEmpty

Можно просто проверить на = ‘’

Функция string.IsNullOrEmpty в справочнике нет. А проверить =’’ надо будет удалить все пробелы. Функция длина тоже в этом случае поможет. Но если был би отдельная функция, то еще лучше. В других языках есть.

Потому что вы не в той справке смотрите.
https://social.msdn.microsoft.com/search/en-us?query=string.IsNullOrEmpty%20&ac=4

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

Так а сразу сказать что строку с пробелами надо считать пустой? Значит .Trim перед проверкой.

значит string.IsNullOrWhiteSpace

1 лайк

Когда Вы работаете с .NET-языком, нужно быть готовым, что в его Справке много чего нет, потому что существенная часть вещей реализована в .NET-библиотеках. В частности, там много классовых методов, а это означает, что надо ввести имя класса (а не имя переменной), поставить точку - и Intellisence откроет Вам большой перечень возможностей.

А если не нравится - используйте

not s.IsMatch('\S')