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


#2118

Скажите, 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.

#2119

В неправильном использовании 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 мс - это длительность одного отсчета (“тика”), т.е. погрешность.


#2120

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


#2121
  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 (если их менять местами целиком или по частям). Но это всё же не случайность, их даёт стабильно. В общем передаю эстафету вам, братья форумчане))


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

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

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


#2123

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

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


#2124

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


#2125

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


#2126

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


#2127

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

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

P.S.

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

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


#2128

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

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


#2129

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

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

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


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

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

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

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

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


#2132

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


#2133

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


#2134

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


#2135

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


#2136

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

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

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


#2137

значит string.IsNullOrWhiteSpace


#2138

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

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

not s.IsMatch('\S')