GR32_System.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. unit GR32_System;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Andre Beckedorf, Michael Hansen <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2009
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$include GR32.inc}
  33. uses
  34. {$ifndef FPC}
  35. System.Diagnostics,
  36. {$endif}
  37. GR32.CPUID;
  38. // Types; // Not really needed in this unit but we do need something in this uses list
  39. // TODO : Why?
  40. //------------------------------------------------------------------------------
  41. //
  42. // Delphi compatible TStopwatch-lookalike
  43. //
  44. //------------------------------------------------------------------------------
  45. // Differences from the Delphi TStopwatch:
  46. // - Does not have the Elapsed:TTimeSpan property.
  47. //------------------------------------------------------------------------------
  48. {$if not defined(FPC)}
  49. type
  50. TStopwatch = System.Diagnostics.TStopWatch;
  51. {$else}
  52. type
  53. TStopwatch = record
  54. strict private
  55. class var FFrequency: Int64;
  56. class var FIsHighResolution: Boolean;
  57. class var FTickFrequency: Double;
  58. strict private
  59. FElapsed: Int64;
  60. FRunning: Boolean;
  61. FStartTimeStamp: Int64;
  62. function GetElapsedDateTimeTicks: Int64;
  63. function GetElapsedMilliseconds: Int64;
  64. function GetElapsedTicks: Int64;
  65. class constructor Create;
  66. public
  67. class function Create: TStopwatch; static;
  68. class function GetTimeStamp: Int64; static;
  69. procedure Reset;
  70. procedure Start;
  71. class function StartNew: TStopwatch; static;
  72. procedure Stop;
  73. property ElapsedMilliseconds: Int64 read GetElapsedMilliseconds;
  74. property ElapsedTicks: Int64 read GetElapsedTicks;
  75. class property Frequency: Int64 read FFrequency;
  76. class property IsHighResolution: Boolean read FIsHighResolution;
  77. property IsRunning: Boolean read FRunning;
  78. end;
  79. {$ifend}
  80. {$ifndef FPC}
  81. type
  82. TStopwatchHelper = record helper for TStopwatch
  83. {$endif}
  84. const
  85. TicksPerMicrosecond = 10; // 1 tick = 100ns
  86. TicksPerNanosecond = TicksPerMicrosecond / 1000;
  87. TicksPerMillisecond = 1000 * Int64(TicksPerMicrosecond);
  88. TicksPerSecond = 1000 * Int64(TicksPerMillisecond);
  89. {$ifndef FPC}
  90. end;
  91. {$endif}
  92. //------------------------------------------------------------------------------
  93. //
  94. // Performance timer
  95. //
  96. //------------------------------------------------------------------------------
  97. // Obsolete; Use TStopWatch instead.
  98. //------------------------------------------------------------------------------
  99. type
  100. TPerfTimer = class
  101. private
  102. FStopwatch: TStopwatch;
  103. public
  104. procedure Start;
  105. function ReadNanoseconds: string;
  106. function ReadMilliseconds: string;
  107. function ReadSeconds: string;
  108. function ReadValue: Int64;
  109. end deprecated 'Use TStopwatch';
  110. var
  111. {$WARN SYMBOL_DEPRECATED OFF}{$ifdef FPC}{$push}{$endif}
  112. GlobalPerfTimer: TPerfTimer deprecated 'Use TStopwatch';
  113. {$ifndef FPC}{$WARN SYMBOL_DEPRECATED DEFAULT}{$else}{$pop}{$endif}
  114. //------------------------------------------------------------------------------
  115. //
  116. // Portable GetTickCount
  117. //
  118. //------------------------------------------------------------------------------
  119. { Pseudo GetTickCount implementation for Linux - for compatibility
  120. This works for basic time testing, however, it doesnt work like its
  121. Windows counterpart, ie. it doesnt return the number of milliseconds since
  122. system boot. Will definitely overflow. }
  123. function GetTickCount: UInt64;
  124. //------------------------------------------------------------------------------
  125. //
  126. // Processor and core management
  127. //
  128. //------------------------------------------------------------------------------
  129. { Returns the number of processors configured by the operating system. }
  130. function GetProcessorCount: Cardinal;
  131. // Set process affinity to exclude efficiency cores
  132. function SetPerformanceAffinityMask(Force: boolean = False): boolean;
  133. procedure RestoreAffinityMask;
  134. //------------------------------------------------------------------------------
  135. //
  136. // Legacy CPU features
  137. //
  138. //------------------------------------------------------------------------------
  139. // Legacy HasInstructionSet and CPUFeatures functions
  140. //------------------------------------------------------------------------------
  141. type
  142. { TCPUFeature, previously TCPUInstructionSet, defines specific CPU
  143. technologies. Note that deprecated features has been removed; Specifically
  144. ciMMX, ciEMMX, ci3DNow, and ci3DNowExt. }
  145. TCPUFeature = (ciSSE, ciSSE2);
  146. TCPUFeatures = set of TCPUFeature;
  147. PCPUFeatures = ^TCPUFeatures;
  148. { General function that returns whether a particular instruction set is
  149. supported for the current CPU or not }
  150. function HasInstructionSet(const InstructionSet: TCPUFeature): Boolean; deprecated 'Use CPU.InstructionSupport instead';
  151. function CPUFeatures: TCPUFeatures; deprecated 'Use CPU.InstructionSupport instead';
  152. const
  153. InstructionSetMap: array[TCPUFeature] of TCPUInstructionSet = (isSSE, isSSE2);
  154. // Migration support: TCPUFeatures->TInstructionSupport
  155. function CPUFeaturesToInstructionSupport(CPUFeatures: TCPUFeatures): TInstructionSupport; deprecated;
  156. //------------------------------------------------------------------------------
  157. //
  158. // CPU features
  159. //
  160. //------------------------------------------------------------------------------
  161. // For use in CPU dispatch bindings
  162. //------------------------------------------------------------------------------
  163. var
  164. CPU: TCPU;
  165. //------------------------------------------------------------------------------
  166. //------------------------------------------------------------------------------
  167. //------------------------------------------------------------------------------
  168. implementation
  169. uses
  170. {$if not defined(FPC)}
  171. {$ifdef MSWINDOWS}
  172. WinApi.Windows,
  173. {$endif}
  174. System.SysUtils,
  175. System.Classes;
  176. {$else}
  177. {$ifdef MSWINDOWS}
  178. Windows,
  179. {$endif}
  180. SysUtils,
  181. Classes;
  182. {$ifend}
  183. //------------------------------------------------------------------------------
  184. //
  185. // GetTickCount
  186. //
  187. //------------------------------------------------------------------------------
  188. {$ifndef FPC}
  189. var
  190. TickCounter: TStopwatch;
  191. function GetTickCount: UInt64;
  192. begin
  193. Result := UInt64(TickCounter.ElapsedMilliseconds);
  194. end;
  195. {$else}
  196. function GetTickCount: UInt64;
  197. begin
  198. Result := SysUtils.GetTickCount64;
  199. end;
  200. {$endif}
  201. //------------------------------------------------------------------------------
  202. //
  203. // TStopwatch
  204. //
  205. //------------------------------------------------------------------------------
  206. {$if defined(FPC)}
  207. class constructor TStopwatch.Create;
  208. begin
  209. {$if defined(MSWINDOWS)}
  210. if not QueryPerformanceFrequency(FFrequency) then
  211. begin // Never happens on XP and later
  212. FIsHighResolution := False;
  213. FFrequency := TicksPerSecond;
  214. FTickFrequency := 1.0;
  215. end else
  216. begin
  217. FIsHighResolution := True;
  218. FTickFrequency := 10000000.0 / FFrequency;
  219. end;
  220. {$elseif defined(POSIX)}
  221. FIsHighResolution := True;
  222. FFrequency := 10000000; // 100 ns resolution
  223. FTickFrequency := 10000000.0 / FFrequency;
  224. {$ifend}
  225. end;
  226. class function TStopwatch.Create: TStopwatch;
  227. begin
  228. Result.Reset;
  229. end;
  230. function TStopwatch.GetElapsedDateTimeTicks: Int64;
  231. begin
  232. Result := ElapsedTicks;
  233. if FIsHighResolution then
  234. Result := Trunc(Result * FTickFrequency);
  235. end;
  236. function TStopwatch.GetElapsedMilliseconds: Int64;
  237. begin
  238. Result := GetElapsedDateTimeTicks div TicksPerMillisecond;
  239. end;
  240. function TStopwatch.GetElapsedTicks: Int64;
  241. begin
  242. Result := FElapsed;
  243. if FRunning then
  244. Result := Result + GetTimeStamp - FStartTimeStamp;
  245. end;
  246. class function TStopwatch.GetTimeStamp: Int64;
  247. {$if defined(POSIX) and not defined(MACOS)}
  248. var
  249. res: timespec;
  250. {$ifend}
  251. begin
  252. {$if defined(MSWINDOWS)}
  253. if FIsHighResolution then
  254. QueryPerformanceCounter(Result)
  255. else
  256. // TODO : This looks wrong. GetTickCount always returns ms
  257. Result := GetTickCount64 * UInt64(TicksPerMillisecond);
  258. {$elseif defined(MACOS)}
  259. Result := Int64(AbsoluteToNanoseconds(mach_absolute_time) div 100);
  260. {$elseif defined(POSIX)}
  261. clock_gettime(CLOCK_MONOTONIC, @res);
  262. Result := (Int64(1000000000) * res.tv_sec + res.tv_nsec) div 100;
  263. {$ifend}
  264. end;
  265. procedure TStopwatch.Reset;
  266. begin
  267. FElapsed := 0;
  268. FRunning := False;
  269. FStartTimeStamp := 0;
  270. end;
  271. procedure TStopwatch.Start;
  272. begin
  273. if not FRunning then
  274. begin
  275. FStartTimeStamp := GetTimeStamp;
  276. FRunning := True;
  277. end;
  278. end;
  279. class function TStopwatch.StartNew: TStopwatch;
  280. begin
  281. Result.Reset;
  282. Result.Start;
  283. end;
  284. procedure TStopwatch.Stop;
  285. begin
  286. if FRunning then
  287. begin
  288. FElapsed := FElapsed + GetTimeStamp - FStartTimeStamp;
  289. FRunning := False;
  290. end;
  291. end;
  292. {$ifend}
  293. //------------------------------------------------------------------------------
  294. //
  295. // Performance timer
  296. //
  297. //------------------------------------------------------------------------------
  298. function TPerfTimer.ReadNanoseconds: string;
  299. begin
  300. Result := IntToStr(Round(FStopwatch.ElapsedTicks / {$ifndef FPC}FStopwatch.{$endif}TicksPerNanosecond));
  301. end;
  302. function TPerfTimer.ReadMilliseconds: string;
  303. begin
  304. Result := FloatToStrF(FStopwatch.ElapsedTicks / {$ifndef FPC}FStopwatch.{$endif}TicksPerMillisecond, ffFixed, 15, 3);
  305. end;
  306. function TPerfTimer.ReadSeconds: String;
  307. begin
  308. Result := FloatToStrF(FStopwatch.ElapsedTicks / {$ifndef FPC}FStopwatch.{$endif}TicksPerSecond, ffFixed, 15, 3);
  309. end;
  310. function TPerfTimer.ReadValue: Int64;
  311. begin
  312. Result := FStopwatch.ElapsedTicks;
  313. end;
  314. procedure TPerfTimer.Start;
  315. begin
  316. FStopwatch := TStopwatch.StartNew;
  317. end;
  318. //------------------------------------------------------------------------------
  319. //
  320. // Processor and core management
  321. //
  322. //------------------------------------------------------------------------------
  323. function GetProcessorCount: Cardinal;
  324. {$ifndef FPC}
  325. begin
  326. Result := CPUCount;
  327. end;
  328. {$else}
  329. {$if defined(MSWINDOWS)}
  330. var
  331. lpSysInfo: TSystemInfo;
  332. begin
  333. GetSystemInfo(lpSysInfo);
  334. Result := lpSysInfo.dwNumberOfProcessors;
  335. end;
  336. {$elseif defined(UNIX)}
  337. begin
  338. Result := 1;
  339. end;
  340. {$ifend}
  341. {$endif}
  342. //------------------------------------------------------------------------------
  343. {$if (defined(MSWINDOWS)) and (not defined(FPC))}
  344. function SetPerformanceAffinityMask(Force: boolean): boolean;
  345. type
  346. // Declaration in Delphi 11 lacks EfficiencyClass
  347. TProcessorRelationship = record
  348. Flags: BYTE;
  349. EfficiencyClass: BYTE;
  350. Reserved: array[0..19] of BYTE;
  351. GroupCount: WORD;
  352. GroupMask: array[0..0] of GROUP_AFFINITY;
  353. end;
  354. var
  355. ProcessHandle: THandle;
  356. ProcessMask, SystemMask: NativeUInt;
  357. NewMask: NativeUInt;
  358. Size: Cardinal;
  359. ProcessorInfoBuffer: TBytes;
  360. ProcessorInfo: PSystemLogicalProcessorInformationEx;
  361. EfficiencyMap: array[Byte] of KAFFINITY;
  362. CoreMask: ^KAFFINITY;
  363. i: integer;
  364. begin
  365. Result := False;
  366. // TProcessorRelationship.EfficiencyClass requires Windows 10
  367. if (not CheckWin32Version(10, 0)) then
  368. exit;
  369. ProcessHandle := GetCurrentProcess();
  370. GetProcessAffinityMask(ProcessHandle, ProcessMask, SystemMask);
  371. // Punt if mask has already been modified
  372. if (not Force) and (ProcessMask <> SystemMask) then
  373. exit;
  374. Size := 0;
  375. if (not GetLogicalProcessorInformationEx(RelationProcessorCore, nil, Size)) then
  376. if (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
  377. exit;
  378. SetLength(ProcessorInfoBuffer, Size);
  379. ProcessorInfo := @ProcessorInfoBuffer[0];
  380. if (not GetLogicalProcessorInformationEx(RelationProcessorCore, PSystemLogicalProcessorInformation(ProcessorInfo), Size)) then
  381. exit;
  382. ZeroMemory(@EfficiencyMap, SizeOf(EfficiencyMap));
  383. // For each efficiency class create a core mask
  384. while (Size > 0) do
  385. begin
  386. if (ProcessorInfo.Relationship = RelationProcessorCore) then
  387. begin
  388. CoreMask := @EfficiencyMap[TProcessorRelationship(ProcessorInfo.Processor).EfficiencyClass];
  389. for i := 0 to ProcessorInfo.Processor.GroupCount-1 do
  390. CoreMask^ := CoreMask^ or ProcessorInfo.Processor.GroupMask[i].Mask;
  391. end;
  392. Dec(Size, ProcessorInfo.Size);
  393. Inc(PByte(ProcessorInfo), ProcessorInfo.Size);
  394. end;
  395. // Create a mask for performance cores
  396. NewMask := 0;
  397. i := 0;
  398. while (i < High(EfficiencyMap)) do
  399. begin
  400. if (EfficiencyMap[i] <> 0) then
  401. begin
  402. // Assume the first performance class is "efficiency". Skip it.
  403. Inc(i);
  404. while (i <= High(EfficiencyMap)) do
  405. begin
  406. NewMask := NewMask or EfficiencyMap[i];
  407. Inc(i);
  408. end;
  409. break;
  410. end;
  411. Inc(i);
  412. end;
  413. // Set the new mask
  414. NewMask := SystemMask and NewMask;
  415. if (NewMask <> 0) and (NewMask <> ProcessMask) then
  416. begin
  417. SetProcessAffinityMask(ProcessHandle, NewMask);
  418. Result := True;
  419. end;
  420. end;
  421. procedure RestoreAffinityMask;
  422. var
  423. ProcessHandle: THandle;
  424. ProcessMask, SystemMask: NativeUInt;
  425. begin
  426. ProcessHandle := GetCurrentProcess();
  427. GetProcessAffinityMask(ProcessHandle, ProcessMask, SystemMask);
  428. if (ProcessMask <> SystemMask) then
  429. SetProcessAffinityMask(ProcessHandle, SystemMask);
  430. end;
  431. {$else}
  432. function SetPerformanceAffinityMask(Force: boolean): boolean;
  433. begin
  434. Result := False;
  435. end;
  436. procedure RestoreAffinityMask;
  437. begin
  438. end;
  439. {$ifend}
  440. //------------------------------------------------------------------------------
  441. //
  442. // Legacy CPU features
  443. //
  444. //------------------------------------------------------------------------------
  445. function CPUFeaturesToInstructionSupport(CPUFeatures: TCPUFeatures): TInstructionSupport;
  446. var
  447. InstructionSet: TCPUFeature;
  448. begin
  449. Result := [];
  450. for InstructionSet in CPUFeatures do
  451. Include(Result, InstructionSetMap[InstructionSet]);
  452. end;
  453. //------------------------------------------------------------------------------
  454. function HasInstructionSet(const InstructionSet: TCPUFeature): Boolean;
  455. begin
  456. {$IFNDEF PUREPASCAL}
  457. Result := (InstructionSetMap[InstructionSet] in CPU.InstructionSupport);
  458. {$ELSE}
  459. Result := False;
  460. {$ENDIF}
  461. end;
  462. //------------------------------------------------------------------------------
  463. function CPUFeatures: TCPUFeatures;
  464. var
  465. InstructionSet: TCPUFeature;
  466. begin
  467. Result := [];
  468. for InstructionSet := Low(TCPUFeature) to High(TCPUFeature) do
  469. if (InstructionSetMap[InstructionSet] in CPU.InstructionSupport) then
  470. Include(Result, InstructionSet);
  471. end;
  472. //------------------------------------------------------------------------------
  473. //------------------------------------------------------------------------------
  474. //------------------------------------------------------------------------------
  475. initialization
  476. {$ifndef FPC}
  477. TickCounter := TStopwatch.StartNew;
  478. {$endif}
  479. CPU := TCPU.GetCPUInfo;
  480. {$WARN SYMBOL_DEPRECATED OFF}{$ifdef FPC}{$push}{$endif}
  481. GlobalPerfTimer := TPerfTimer.Create;
  482. {$ifndef FPC}{$WARN SYMBOL_DEPRECATED DEFAULT}{$else}{$pop}{$endif}
  483. finalization
  484. {$WARN SYMBOL_DEPRECATED OFF}{$ifdef FPC}{$push}{$endif}
  485. GlobalPerfTimer.Free;
  486. {$ifndef FPC}{$WARN SYMBOL_DEPRECATED DEFAULT}{$else}{$pop}{$endif}
  487. end.