Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Auslesen von WMI dauert (zu?) lange (https://www.delphipraxis.net/204257-auslesen-von-wmi-dauert-zu-lange.html)

Shark99 11. Mai 2020 22:08


Auslesen von WMI dauert (zu?) lange
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich bin dabei die Startzeit eines alten Delphi 7 Programms zu optimieren. Deshalb bin ich den ganzen Startvorgang akribisch durchgegangen. Während des Starts wird ein separater Thread erzeugt um verschiedene WMI Werte auszulesen. Weil die UI gesperrt ist bis der Thread ein mal durch ist, verzögert sich der Programmstart um bis zu 3 Sekunden. Es ist aber nicht konsistent. Meist wird der Start um 0.5-0.7 Sekunden verzögert, mit Ausreißern nach oben wenn man den Start oft protokolliert.

Um weiter zu testen habe ich einen der WMI-Aufrufe in ein Testprogramm verfrachtet. Dort mache ich einen einzigen WMI Query und messe dabei die Zeit. Es dauert tatsächlich etwa 300ms pro Query. Mit Pech dauert es 3 Sekunden. Bei hunderten von Starts hatte ich sogar einen Fall wo der erste Aufruf 27 Sekunden brauchte (die folgenden wieder 300ms).

Was ist der Grund wieso der Query so lange dauert? Ist der Code fehlerhaft? Einen Integer aus einer lokalen Datenbank auszulesen sollte doch maximal 10ms dauern.

In einem weiteren Test habe ich ohne einen separaten Thread getestet und dabei bemerkt, dass während der Query läuft man das Fenster nicht verschieben kann. Was ist der Grund? Wird der Message Handler angehalten? Kenne mich leider wenig mit WMI aus. Wird hier mit Callbacks gearbeitet?

Beispielprojekt mit kompilierter Exe habe ich angehängt.

Code:
function VarIsNothing(V: OleVariant): Boolean;
begin
  Result :=
    (TVarData(V).VType = varDispatch)
    and
    (TVarData(V).VDispatch = nil);
end;

function WMI_GetCPUPerformanceData:boolean;
const
  wbemFlagReturnImmediately = $00000010;
  wbemFlagForwardOnly = $00000020;

  Query_PerfFormattedData_Counters_ProcessorInformation_Total = 'SELECT * FROM Win32_PerfFormattedData_Counters_ProcessorInformation Where Name="_Total"';
var
  WbemLocator: OLEVariant;
  WMIService: OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject: OLEVariant;
  oEnum: IEnumvariant;
  dValue: LongWord;
  vArray: variant;
  logtime: TDateTime;
begin;
  Result := False;

  logtime := now;

  try
    try
      WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      WMIService := WbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');

      FWbemObjectSet := WMIService.ExecQuery(Query_PerfFormattedData_Counters_ProcessorInformation_Total, 'WQL', wbemFlagReturnImmediately and wbemFlagForwardOnly);
      oEnum := IUnknown(FWbemObjectSet._NewEnum) as IEnumvariant;
      if oEnum.Next(1, FWbemObject, dValue) = 0 then
      begin
        if not VarIsNothing(FWbemObject) then
        begin
          iCPUFrequency := LongWord(FWbemObject.ProcessorFrequency);
          Result := True;
        end;
      end;
    except
      bCPUFrequencyAvailable := False;
    end;
  finally
    FWbemObject := Unassigned;
    sExecutiontime := inttostr(round((now - logtime) * 86400000));
  end;
end;

himitsu 12. Mai 2020 03:18

AW: Auslesen von WMI dauert (zu?) lange
 
Zitat:

Weil die UI gesperrt ist bis der Thread ein mal durch ist
Also ist der Thread komplett umsonst? Weil sowieso gewartet wird. :freak:


Schonmal überlegt, dass es vielleicht dauert, bis die Werte berechnet/gezählt sind?

Wobei WMI sowieso nie darauf ausgelegt war schnell zu sein.


PS:
Delphi-Quellcode:
SELECT ProcessorFrequency FROM ...
:stupid:

Was erwartest du von dem AND in
Delphi-Quellcode:
wbemFlagReturnImmediately and wbemFlagForwardOnly
?

Und was willst du mit der CPU-Geschwindigkeit?
Eventuell gibt es was Besseres, für deinen Anwendungsfall.
GetProcessTimes, MSDN-Library durchsuchenGetSystemTimes, PerformenceCounters usw.

Shark99 12. Mai 2020 22:19

AW: Auslesen von WMI dauert (zu?) lange
 
Es ist nicht meine App, habe nur den Optimierungsauftrag bekommen. So wie es sehe wurde in alter Version ein TTimer verwendet um mehrere Werte per WMI zu holen, welche dem User angezeigt werden. Hier trat das Problem auf, dass 30-50% jeder Sekunde der Message Queue von dem Query gelockt wird (Callback?). Das Fenster hängt. Deshalb wurde das ganze in einen TThread verschoben. Der Query blockiert aber den Message Queue auch im Thread, nur nicht ganz so schlimm wie in einem TTimer. Der Thread macht ja schon Sinn, weil jede Sekunde die Werte neu ausgelesen werden.

Heute habe ich Tests mit der Powershell gemacht (Get-CimInstance -Class Win32_PerfRawData_Counters_ProcessorInformation). Dort ist auch alles arschlahm. WMI scheint totaler Müll zu sein!

>Was erwartest du von dem AND in wbemFlagReturnImmediately and wbemFlagForwardOnly ?

Ist nicht mein Code. Ich weiß nicht was hier gemacht wird. Laut MS Docs wird so ein Semisynchronous Call erzeugt, der auch schneller sein sollte.

>Und was willst du mit der CPU-Geschwindigkeit?
>Eventuell gibt es was Besseres, für deinen Anwendungsfall.
>GetProcessTimes, MSDN-Library durchsuchenGetSystemTimes, PerformenceCounters usw.

Diese wird erstmals angezeigt. Funktioniert nur richtig wenn der PC nicht übertaktet wird (sonst aber keine andere Möglichkeit den Wert zu bekommen ohne einen sys Treiber).
Dann kann der User noch konditionell Scripte starten wenn die Last gering ist. Deshalb braucht man den Takt, CPU Usage reicht alleine nicht aus.

himitsu 12. Mai 2020 22:43

AW: Auslesen von WMI dauert (zu?) lange
 
Zitat:

Delphi-Quellcode:
wbemFlagReturnImmediately and wbemFlagForwardOnly

Mathematik:
Delphi-Quellcode:
$0001 and $0008 = 0 // nichts
$0001 or $0008 = $0009 // Beides

Der Takt hat aber keine Aussagekraft darüber, ob verwendbare Rechenzeiten übrig sind.
Die Zeiten können das aber sagen, unabhängig vom CPU-Takt.

Ein niedriger Takt sagt nur aus, dass schon längere Zeit kaum etwas gerechnet wird,
aber es kann auch aussagen, dass die CPU viel zu sehr ausgelastet ist und wegen Übertemperatur in den Schutzmodus versetzt wurde,
oder ...

Bei diesem PC hier ist es sogar so, dass der aktuell nicht auf "Leistung" optimiert ist, sondern auf Energieverbrauch und Lautstärke. (nur wenn längere Zeit voll ausgelastet, wird hochgetaktet).
Also meistens kann man dem Takt garnicht ansehen, ob und wie hoch die Auslastung ist.

Bei den CPU-Zeiten ist es dagegen recht einfach.
* 4 Kerne haben 400% Rechenzeit
* Wenn also in einer Minute alle Zeiten zusammengerechnet nur 2 Minuten ergeben, ist die CPU somit zu mindestens 50% arbeitslos gewesen. (noch mehr arbeitslos, wenn der Takt noch höher werden kann, aber das ist egal)
* CPUZeiten / Messinterval / AnzahlDerKerne * 100 = Auslastung in Prozenz
* (CPUZeitenJetzt - CPUZeitenLetztesInterval) / (ZeitJetzt - ZeitLetzteMessung) / AnzahlDerKerne * 100 = Auslastung in Prozenz

Diese Berechnung hilft aber nur, wenn man multithreaded arbeitet.
Bei Single-Thread könnte man statt der Zeiten aller Kerne nur den Kern mit der meisten Rechenzeit nehmen, bzw. die freien Zeiten des Kerns, auf dem man grade läuft, bzw. besser die Zeiten seines eigenen Threads/Prozesses.

Shark99 12. Mai 2020 23:06

AW: Auslesen von WMI dauert (zu?) lange
 
Hab heute mehrere Stunden zu dem Thema gegoogelt, weil ich bei dem Projekt von WMI wegkommen wollte. Derzeit bleibe ich bei WMI, weil viele Werte wie z.B. DPCRate aus Win32_PerfFormattedData_Counters_ProcessorInformat ion nicht ohne WMI oder Kerneltreiber zu emitteln sind.

Bei der CPU Frequenz ist alles sehr problematisch. Irgendwelche Takte zu zählen ist genauso ungenau wie die WMI Werte. Meine Kiste ist von 4 auf 4.6 Ghz übertaktet. Die CPU Frequenz wird von CPU-Z und HWInfo64 korrekt als 4,6 GHz unter Last angezeigt, WMI und die Taktzählerei über Performance Counters ergibt 4.0 GHz. Richtige Werte bekommt man nur über einen Kernel Treiber, was CPUZ und HWInfo 64 ja machen.

Wenn eine x86 CPU überhitzt und drosselt geht sie nicht auf Minimaltakt runter (d.h. unter den Nominaltakt, Turbo bleibt natürlich aus), was bei vielen ARM Prozessoren der Fall ist, sondern lässt Takte aus. Es gibt ein hin und her zwischen C-States und Nominaltakt. CPUZ etc zeigen dann aber konstant den Nominaltakt an.

Wenn weniger als 1GHz anliegt hat die CPU entweder nichs zu tun, oder Stromsparprofil ist aktiv. Wenn weniger als 1GHz anliegt und das Delta von TSystem_Performance_Information64.IdleTime in Vergleich zu Systemtime unter 10% liegt kann man sicher davon ausgehen dass die CPU idle ist. Die alleinige Betrachtung der Idletime reicht auf keinem Fall aus, weil sich die Prozentwerte zu Systemtime zwischen 800Mhz und 4GHz nicht ändern. Ein Idle-Wert von 20% bei 800MHz hat ja eine ganz andere Bedeutung wie ein Idle-Wert von 20% bei 4GHz.

Shark99 12. Mai 2020 23:12

AW: Auslesen von WMI dauert (zu?) lange
 
Zitat:

Zitat von himitsu (Beitrag 1464420)
Zitat:

Delphi-Quellcode:
wbemFlagReturnImmediately and wbemFlagForwardOnly

Mathematik:
Delphi-Quellcode:
$0001 and $0008 = 0 // nichts
$0001 or $0008 = $0009 // Beides

Ich nehme an der Code wurde von Google übernommen z.B von hier

https://stackoverrun.com/de/q/597183

Wahrscheinlich falsch übersetzt aus MS VB Beispielen. Aus wbemFlagReturnImmediately + wbemFlagForwardOnly in VB wurde ein AND in Delphi. p.s. Ändert aber nichts an der Trägheit des Query. Kein Unterschied zwischen $0 und $9.

himitsu 12. Mai 2020 23:59

AW: Auslesen von WMI dauert (zu?) lange
 
Nja, ein + gibt es auch im Delphi, aber 4 + 4 = 4, was man nur mit dem OR richtig hinbekommt.

Delphi-Quellcode:
loc := TSWbemLocator.Create(nil);
Services := Loc.ConnectServer(...);
Den Teil könnte man auslagern und anstatt es jedes Mal neu zu machen nur einmal am Anfang, aber der Teil braucht hier auch kaum Zeit.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:10 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz