Войти
ФлеймФорумПрограммирование

"Амперметр"

#0
10:59, 5 июня 2019

  Я раньше немного пользовался AQTime для Delphi, в частности для оптимизации кода. AQTime вроде как должен показывать участки кода, которые надо оптимизировать. Но я не очень верю в этот подход, потому что для измерения скорости нужно ставить в коде счётчики, а счётчики сами отнимают ресурсы. Мне интуитивно кажется что такие вещи, как работа счётчиков, необходимо контролировать самому.
Поэтому я написал такой объект на Delphi:

type
  TSpeedManager=record
    FSession:record
      CurTagsCount:integer;
      AllTagsCount:integer;
      Tags:array[0..maxsessiontagscount-1] of record
        RunsCount:int64;
        RunsSumTimeMs:int64;
        LastTime:longword;
      end;
      SessionActivationTime:longword;
      SessionFinalizationTime:longword;
    end;
    FSessionFinalized:boolean;
    FReport:tstringlist;

    procedure WriteSessionReport;
    procedure SaveReportToFile;
    procedure StartSession(alltagscount:integer);
    procedure FinalizeSession;
    procedure PlaceTag(tagnum:integer);
    procedure Initialize;
    procedure Finalize;
  end;

var
  ProjectSpeedManager:TSpeedManager;

В моём коде этот объект используется, например, вот так:

function TLinearTrianlesPlaneUnpackedModel.CalculateQPart(pointx,pointy:integer):double;
  begin
    projectspeedmanager.StartSession(4);
    projectspeedmanager.PlaceTag(1);
    result:=CalculateQPartR(pointx,pointy)*(1-FKDeriv2);
    projectspeedmanager.PlaceTag(2);
    result:=result+CalculateQPartDN(pointx,pointy)*FKDeriv2;
    projectspeedmanager.PlaceTag(3);
    projectspeedmanager.FinalizeSession;
  end;

Это код для МНК-минимизации некоего функционала; функционал состоит из двух слагаемых, соответственно считается двумя разными функциями. В данном участке считается не сам функционал, а его градиент по конкретному параметру, и градиент также состоит из двух слагаемых.
Когда программа отработала, projectspeedmanager сохранил текстовой файл отчёт, из которого я узнал, что код между тагами 1 и 2 занял 68% всего времени, а код между тэгами 2 и 3 – 31% времени. Соответственно я теперь знаю, что для оптимизации нужно прежде всего ускорить функцию CalculateQPartR, и в ней я также могу разместить тэги в разных её участках, чтобы узнать какие участки самые проблемные, потом разместить тэги и в них и т.д. Также projectspeedmanager сообщил мне, что выполнение всего кода между StartSession и FinalizeSession заняло 82% всего времени работы программы, точнее кода, который вызывал эту функцию CalculateQPartR (я и сам уже знал что функция CalculateQPartR лимитирующая, но на всякий случай полезно такие вещи проверять).
Это пока работает скорее в самых простых случаях, а мне придётся ещё приспосабливать мой амперметр для более сложных вариантов, например когда часть счётчиков находится внутри цикла, а часть за его пределами. Возможно тут будут нужны какие-то вложенные сессии, каждая со своим набором счётчиков.
В дальнейшем, возможно, потребуется сделать ещё вот что: “прокалибровать” мой амперметр, чтобы знать, сколько времени отнимает вызов счётчиков, и учесть это в пересчёте процентов, отданных каждому участку кода.
Можете подсказать, как аналогичный объект будет выглядеть на C++?

#1
(Правка: 11:19) 11:15, 5 июня 2019

Vit Nhoc
> Можете подсказать, как аналогичный объект будет выглядеть на C++?
gcc -pg

#2
(Правка: 13:24) 13:20, 5 июня 2019

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

Profiler = class
   private type
    TPerformanceSlot = class
      counter:Double;
      StartTime:Int64;
      cName  : string;
      entered:boolean;
      avg : Double;
      maxv: Double;
      minv: Double;
      avgcounter:integer;
      percent:Double;
      useprecentage:boolean;
    end;
   private class var
     Counters : TObjectDictionary<string, TPerformanceSlot>;
     CPUFrequency:Int64;
     TBS : TStringBuilder;
     txfile : TextFile;
     cached:TArray<TPerformanceSlot>;
     collector:Double;
     CS:TCriticalSection;
   class constructor ClassCreate;
   class destructor ClassDestroy;
   private
    class function MakeSlot(SampleName:string):TPerformanceSlot;
    class function ToString(PS:TPerformanceSlot):string;
   public
    class function GetCachedNumSlots:integer;
    class procedure CachAndSortResults;
    class function CachedName(index:integer):string;
    class function CachedAvg(index:integer):Double;
    class function CachedTime(index:integer):Double;
    class function CachedPercent(index:integer):Double;
    class function CachedMax(index:integer):Double;
    class function CachedMin(index:integer):Double;
    class procedure ClearStatistics;
    class procedure BeginSample(SampleName:string; useprecentage:boolean=true);
    class procedure EndSample(SampleName:string);
    class procedure Print(NumItems:integer = 0);overload;
    class procedure Print(toList:TStrings; dosort:boolean);overload;
 end;

используется так

{$IFDEF PROFILING}Profiler.BeginSample('MyTask');{$ENDIF}
do something
{$IFDEF PROFILING}Profiler.EndSample('MyTask');{$ENDIF}
дальше функции для сбора и обработки результатов, можно в отдельном потоке даже отслеживать

ФлеймФорумПрограммирование