Home > Delphi, Did You Know > Form Load Profiler: High Precision Timer

Form Load Profiler: High Precision Timer


Pada beberapa kali kesempatan, saya menyajikan bagaimana mengukur waktu tayang (load-time) suatu form dari form tersebut di buat hingga benar – benar tampil dan siap digunakan. Beberapa variasi sudah saya sajikan, mulai dari pengukuran form tunggal hingga banyak form dengan menggunakan teknik interposer class maupun class helper. Ketiga variasi artikel tersebut menggunakan metode pengukuran waktu yang sama, yaitu menggunakan fungsi GetTickCount. Fungsi GetTickCount memiliki ketelitian hingga hitungan mili-detik (ms). Untuk kebutuhan secara umum, GetTickCount sudah mencukupi, namun adakalanya pengukuran yang dilakukan membutuhkan ketelitian yang lebih, misalnya dalam nano-detik. Disinilah GetTickCount tidak bisa memenuhinya.

Windows menyediakan 2 (dua) API yang berkaitan dengan pengukuran dengan ketelitian tinggi, yaitu QueryPerformanceCounter dan QueryPerformanceFrequency. Kedua fungsi ini digunakan saling melengkapi.

BOOL QueryPerformanceCounter(
LARGE_INTEGER *lpPerformanceCount // address of current counter value
);

Parameters

lpPerformanceCount

Points to a variable that the function sets, in counts, to the current performance-counter value. If the installed hardware does not support a high-resolution performance counter, this parameter can be to zero.

Return Values

If the installed hardware supports a high-resolution performance counter, the return value is nonzero.
If the installed hardware does not support a high-resolution performance counter, the return value is zero.

BOOL QueryPerformanceFrequency(
LARGE_INTEGER *lpFrequency // address of current frequency
);

Parameters

lpFrequency

Points to a variable that the function sets, in counts per second, to the current performance-counter frequency. If the installed hardware does not support a high-resolution performance counter, this parameter can be to zero.

Return Values

If the installed hardware supports a high-resolution performance counter, the return value is nonzero.
If the installed hardware does not support a high-resolution performance counter, the return value is zero.

Dari kutipan diatas, sudah jelas bahwa QueryPerformanceCounter digunakan untuk menampung nilai pencacah / penghitung pada saat API tersebut dipanggil, sedangkan QueryPerformanceFrequency digunakan untuk mendapatkan besaran frekuensi dari pencacah tersebut, berapa nilai cacah yang dihasilkan dalam 1 (satu) detik.

Sehingga untuk mendapatkan nilai pengukuran adalah dengan menghitung nilai cacah setelah pengukuran dikurangi dengan nilai cacah sebelum pengukuran. Nilai tersebut kemudian dibagi dengan frekuensi. Nilai ukur yang didapat adalah dalam satuan detik, untuk mengkonversi dalam satuan lain tinggal disesuaikan. Misalnya untuk dihitung dalam satuan mili-detik maka nilai tersebut dikalikan dengan 1000.

  FElapsedTime := (FEnd - FStart) / FFrequency * 1000;

Integrasi ke Kode

Sekarang saatnya melakukan konversi dari penggunaan API GetTickCount ke QueryPerformanceCounter dan QueryPerformanceFrequency. Disini saya menggunakan kode sumber yang menggunakan teknik interposer class. Untuk penggunakan teknik class helper silahkan disesuaikan sendiri sebagai latihan.

Pertama, deklarasi variabel FStart dan FEnd harus diubah tipe datanya dari Cardinal menjadi Int64. Mengapa Int64? Karena nilai yang ditampung adalah sangat besar mengingat ketelitian presisi yang dihasilkan sangat tinggi, sehingga dibutuhkan tipe data yang lebih besar dari Cardinal (0..4294967295), yaitu Int64 atau LargeInt (-2^63..2^63-1). Kemudian tambahkan variabel untuk menyimpan nilai frekuensi dari pencacah tersebut, yaitu FFrequency, dengan tipe data Int64. Selanjutnya tambahkan variabel untuk menampung nilai hasil pengukuran, yaitu FElapsedTime, dengan tipe data Double.

  protected
    FStart, FEnd : Int64;
    FFrequency   : Int64;
    FElapsedTime : Double;

Perubahan kedua adalah pada constructor Create. Disini perlu dilakukan pemanggilan fungsi QueryPerformanceFrequency untuk mengetahui frekuensi nilai pencacah.

constructor TForm.Create(AOwner: TComponent);
begin
  QueryPerformanceFrequency(FFrequency);
  QueryPerformanceCounter(FStart);
  inherited;
end;

Ketiga, modifikasi method Activate, dan menambahkan perhitungan waktu ukur dalam 1 (satu) mili-detik.

procedure TForm.Activate;
begin
  inherited;
  QueryPerformanceCounter(FEnd);
  FElapsedTime := (FEnd - FStart) / FFrequency * 1000;
end;

Modifikasi keempat adalah modifikasi pada unit – unit lain yang menggunakan unit MultipleInterposerClassHP.pas. Bagian yang diubah adalah cara penyampaian pesan informasi besarnya waktu tayang. Jika sebelumnya menggunakan fungsi IntToStr karena waktu ukur berbasis pada tipe data Cardinal, maka sekarang menjadi FloatToStr karena waktu ukur, yaitu variabel FElapsedTime bertipe data Double. Sebagai contoh unit yang dimodifikasi adalah unit MultipleMainFormUnit.pas.

procedure TMainForm.btnShowResultClick(Sender: TObject);
begin
  lblLoadTime.Caption := ' Form Load Time: ' + FloatToStr(FElapsedTime) + ' ms';
  btnShowResult.Enabled := False;
end;

Sehingga rangkaian kode MultipleInterposerClassHP menjadi sebagai berikut:

{-----------------------------------------------------------------------------
 The contents of this file are subject to the Mozilla Public License
 Version 1.1 (the "License"); you may not use this file except in compliance
 with the License. You may obtain a copy of the License at
 http://www.mozilla.org/MPL/MPL-1.1.html

 Software distributed under the License is distributed on an "AS IS" basis,
 WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
 the specific language governing rights and limitations under the License.

 The Original Code is: MultipleInterposerClassHP.pas, released on 2008-09-30

 The Initial Developer of the Original Code is Bayu Prasetio
 Portions created by Bayu Prasetio are Copyright (C) 2008 Bayu Prasetio.
 All Rights Reserved.
 -----------------------------------------------------------------------------}
unit MultipleInterposerClassHP;

interface

uses
  Classes, Forms;

type
  TForm = class(Forms.TForm)
  protected
    FStart, FEnd : Int64;
    FFrequency   : Int64;
    FElapsedTime : Double;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    procedure Activate; override;
  end;

implementation

{ TForm }

uses
  Windows;

procedure TForm.Activate;
begin
  inherited;
  QueryPerformanceCounter(FEnd);
  FElapsedTime := (FEnd - FStart) / FFrequency * 1000;
end;

constructor TForm.Create(AOwner: TComponent);
begin
  QueryPerformanceFrequency(FFrequency);
  QueryPerformanceCounter(FStart);
  inherited;
end;

end.

Dan sebagai contoh MultipleMainFormUnit.pas menjadi:

{-----------------------------------------------------------------------------
 The contents of this file are subject to the Mozilla Public License
 Version 1.1 (the "License"); you may not use this file except in compliance
 with the License. You may obtain a copy of the License at
 http://www.mozilla.org/MPL/MPL-1.1.html

 Software distributed under the License is distributed on an "AS IS" basis,
 WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
 the specific language governing rights and limitations under the License.

 The Original Code is: MultipleMainFormUnit.pas, released on 2008-09-30

 The Initial Developer of the Original Code is Bayu Prasetio
 Portions created by Bayu Prasetio are Copyright (C) 2008 Bayu Prasetio.
 All Rights Reserved.
 -----------------------------------------------------------------------------}
unit MultipleMainFormUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, MultipleInterposerClassHP;

type
  TMainForm = class(TForm)
    btnShowResult: TButton;
    mmoLegend: TMemo;
    stbMain: TStatusBar;
    btnLoadSecondary: TButton;
    lblLoadTime: TLabel;
    btnLoadAnotherForm: TButton;
    procedure btnShowResultClick(Sender: TObject);
    procedure btnLoadSecondaryClick(Sender: TObject);
    procedure btnLoadAnotherFormClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

{ TSingleForm }

uses
  MultipleSecondaryFormUnit, MultipleAnotherFormUnit;

procedure TMainForm.btnLoadAnotherFormClick(Sender: TObject);
begin
  AnotherForm := TAnotherForm.Create(nil);
  AnotherForm.ShowModal;
  AnotherForm.Free;
end;

procedure TMainForm.btnLoadSecondaryClick(Sender: TObject);
begin
  SecondaryForm := TSecondaryForm.Create(nil);
  SecondaryForm.ShowModal;
  SecondaryForm.Free;
end;

procedure TMainForm.btnShowResultClick(Sender: TObject);
begin
  lblLoadTime.Caption := ' Form Load Time: ' + FloatToStr(FElapsedTime) + ' ms';
  btnShowResult.Enabled := False;
end;

end.

Selengkapnya mengenai demo ini dapat di-unduh di sini: Source Code.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: