-
Content Count
785 -
Joined
-
Last visited
-
Days Won
42
Posts posted by pyscripter
-
-
Your loop calls ExecStrings which means you have the overhead of string compilation. in every iteration
What you can do instead is to wrap you python code in a function:
def calc_outflow(inflow) if (inflow > 6) or (inflow < 2) : return 40 else: return 20
and call the function from Delphi.
Uses VarPyth; script.LoadFromFile('loop.py'); PythonEngine1.ExecStrings(script); var CalcOutFlow: Variant := MainModule.calc_outflow; for i:=1 to i_end do begin Output := CalcOutFlow(Random * 10.0); end;
You will get extra speed if instead of
Output := CalcOupFlow(Random * 10.0);
you call the function using low-level PythonEngine routines e.g. PythonEngine.PyObject_CallObject
-
Somebody had to do it:
[RSP-33448] Memory leaks when using styles - Embarcadero Technologies
Feel free to add to the report.
- 1
- 3
-
Your need to assign the IO before loading the python dll. Otherwise the output is not redirected.
FPythonEngine.IO := FPythonInOut; FPythonEngine.Loaddll;
-
-
Can't you debug? Is PythonInputOutputSendUniData called? If yes why it does not produce output?
-
On 3/1/2021 at 9:18 AM, J. Robroeks said:Installation did throw errors of unavailable packages
The installations issues are probably due to the fact that you are using Delphi 10.4 (is this the case?). Please upgrade to Delphi 10.4.1 or better to 10.4.2
-
Regarding numpy on Linux if you use the default python 3 installation it would suffice to install numpy via pip:
pip3 install numpy
-
Just in case you have not seen it SamplingProfiler 64 – test version - DelphiTools,
I mentioned earlier the updated instrumenting profiler ase379/gpprofile2017: Gpprof with unicode support and new features. (github.com).
Between the two you can get the job done, but I admit being able to use VTune (also free) would be great.
-
The PythonVersions unit is of little value in Linux, where there is no Registry and no registered Python versions. You need to manually provide the DLLName and DLLPath.
You can use some code that searches possible file locations. For example in the Mac you can do, something like:
{$ifdef MACOS}
for N:= 5 to 9 do begin
S:= Format('/Library/Frameworks/Python.framework/Versions/3.%d/lib/libpython3.%d.dylib', [N, N]);
if FileExists(S)
then exit(S);
end;
{$endif}
In Debian Linux you can search for file locations such as:
/usr/lib/x86_64-linux-gnu/libpython3.7m.so
/usr/lib/x86_64-linux-gnu/libpython3.7m.so.1
/usr/lib/x86_64-linux-gnu/libpython3.7m.so.1.0but it may vary by python distribution.
- 1
-
On 3/10/2021 at 5:55 PM, J. Robroeks said:Thank you for your quick and elaborate answer. The py_subject was indeed freed at the wrong place.
In case someone else wonders the following:
There are several funtions in Python that are non-blocking. For example:
- sleep
- urlopen
None of the python code is blocking other threads. But the following functions are blocking:
- PyGILState_Ensure
- PyEval_RestoreThread
In other words you need to get and hold to the GIL to execute any python code. So take for example sleep. It works like windows sleep. But to let other threads to execute python code you need to use Py_Begin_Allow_Threads /Py_End_Allow_Threads before/after the sleep. Python deals with the threads it creates. (threading module).
-
In your code above you should destroy PY_SUBJECT before releasing the GIL
Python has a lesser known feature called sub-interpreters, which allows you to use the interpreter from a clean state. This is what emNewInterpreter does. Normally I would not bother with that.
The pattern you need to follow using the latest version of P4D:
In your main thread to release the GIL after loading the Python dll:
TPythonThread.Py_Begin_Allow_Threads (calls PyEval_SaveThread)
In your Delphi threads including the main thread (or if you use ITask or TParallel) that execute python code (this is what TPythonThread does):
fGILState := .PyGILState_Ensure; try Do python staff finally PyGILState_Release(fGILState); end;
In your main thread before unlolading Python
TPythonThread.Py_End_Allow_Threads (calls PyEval_RestoreThread)
if you have a long running thread that does python stuff and you want allow other threads to do python stuff as well then the pattern is:
fGILState := .PyGILState_Ensure; try Do python staff TPythonThread.Begin_Allow_Threads; try Other threads can run python code finally TPythonThread.End_ALlow_Threads; end; Do more python stuff. finally PyGILState_Release(fGILState); end;
In PyScripter I have a utility function:
type IPyEngineAndGIL = interface function GetPyEngine: TPythonEngine; function GetThreadState: PPyThreadState; property PythonEngine: TPythonEngine read GetPyEngine; property ThreadState: PPyThreadState read GetThreadState; end; function SafePyEngine: IPyEngineAndGIL; begin Result := TPyEngineAndGIL.Create end; type TPyEngineAndGIL = class(TInterfacedObject, IPyEngineAndGIL) fPythonEngine: TPythonEngine; fThreadState: PPyThreadState; fGILState: PyGILstate_STATE; private function GetPyEngine: TPythonEngine; function GetThreadState: PPyThreadState; public constructor Create; destructor Destroy; override; end; { TPyEngineAndGIL } constructor TPyEngineAndGIL.Create; begin inherited Create; fPythonEngine := GetPythonEngine; fGILState := fPythonEngine.PyGILState_Ensure; fThreadState := fPythonEngine.PyThreadState_Get; end; destructor TPyEngineAndGIL.Destroy; begin fPythonEngine.PyGILState_Release(fGILState); inherited; end; function TPyEngineAndGIL.GetPyEngine: TPythonEngine; begin Result := fPythonEngine; end; function TPyEngineAndGIL.GetThreadState: PPyThreadState; begin Result := fThreadState; end;
which is used in the main or other threads as:
var Py: IPyEngineAndGIL;
begin
Py := SafePyEngine;
Py.Engine.
whenever I need to execute Python code
- 1
-
What we now need is a bug report at Embarcadero, with a detailed description of the issue reproduction steps and the potential fixes. @balabuevAre you planning to submit one?
-
26 minutes ago, balabuev said:AFAIK, this internally executed in the context of the main thread (via PostMessage).
Code queued with ForceQueue is executed in the main thread with the same mechanism as Thread.Synchronize and not with PostMessage.
-
@Fr0sT.BrutalSure, you could also optimize DoubleBuffering (if you do use DoubleBuffering). But @Stephen Ball's fix addresses crashes that may occur during connection and disconnection, not the general slowness in painting.
-
9 hours ago, balabuev said:I've replaced the treeview with a frame to remove the mess with the second pair of unneeded scrollbar windows. I've simulated some resource allocation on WM_CREATE in a frame, which is then freed on WM_DESTROY, similar to tree view.
I guess the issue would still be present if you now replace the ListView with anything that has scrollbars including a treeview. Is that right?
-
How about this one?
class procedure TStyleEngine.DoRemoveControl(Control: TWinControl); begin if not (csDestroying in Control.ComponentState) and (FControls <> nil) and FControls.ContainsKey(Control) then begin var Hook := FControls.Items[Control]; TThread.ForceQueue(nil, procedure begin Hook.Free; end); FControls.Remove(Control); end; end;
There may be more efficient solutions, such as adding the hooks for deletion to a List and process that list on idle time or on Hook creation.
- 1
-
In light of @David Heffernan remarks here is a shorter version of @Stephen Ball code:
type TForm1 = class(TForm) procedure CreateWnd; override; procedure WMWTSSessionChange (var Message: TMessage); message WM_WTSSESSION_CHANGE ; procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY; private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.WMDestroy(var Message: TWMDestroy); begin inherited; WTSUnRegisterSessionNotification(Handle); end; procedure TForm1.WMWTSSessionChange(var Message: TMessage); begin case Message.wParam of WTS_SESSION_LOCK, WTS_REMOTE_DISCONNECT: Application.UpdateMetricSettings := False; WTS_REMOTE_CONNECT, WTS_SESSION_UNLOCK: TThread.ForceQueue(nil, procedure begin Application.UpdateMetricSettings := True; end, 30000); end; end; procedure TForm1.CreateWnd; begin inherited; WTSRegisterSessionNotification(Handle, NOTIFY_FOR_THIS_SESSION); end;
Instead of CreateWnd you could override the CreateWindowHandle method.
-
3 hours ago, David Heffernan said:Really? That's upsetting. Although it rings some bells. But it won't matter here because process termination will lead to the system tidying up.
Ill have a look tomorrow.
See DestroyWnd not called at destruction of WinControls - VCL - Delphi-PRAXiS [en]
-
36 minutes ago, David Heffernan said:My answer to this old SO question presents a different way that may be simpler in the long run.
Thanks! DestroyWnd is not called when the application exits. Is this an issue here? You could call the WTSUnRegisterSessionNotification at the WM_DESTROY handler instead.
-
2 hours ago, Stephen Ball said:As @David Millington mentioned, we have been working with the R&D team to share tips learned to help speed up VCL applications under remote desktop - to read about it, including a code sample see:
https://blogs.embarcadero.com/how-to-speed-up-remote-desktop-applications/
@Stephen BallThank you very much!
Is there a need to have a separate window to receive the notifications? Isn't the code below equivalent to yours?
type TForm1 = class(TForm) procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); procedure WMWTSSessionChange (var Message: TMessage); message WM_WTSSESSION_CHANGE ; private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormDestroy(Sender: TObject); begin WTSUnRegisterSessionNotification(Handle); end; procedure TForm1.WMWTSSessionChange(var Message: TMessage); begin inherited; case Message.wParam of WTS_SESSION_LOCK, WTS_REMOTE_DISCONNECT: Application.UpdateMetricSettings := False; WTS_REMOTE_CONNECT, WTS_SESSION_UNLOCK: TThread.ForceQueue(nil, procedure begin Application.UpdateMetricSettings := True; end, 30000); end; end; procedure TForm1.FormCreate(Sender: TObject); begin WTSRegisterSessionNotification(Handle, NOTIFY_FOR_THIS_SESSION); end;
-
11 minutes ago, balabuev said:First one will be deleted by window manager, but the second - not.
This is not true. StyleEngine.Destroys calls FreeControlHooks which destroys all remaining hooks. However what @Attila Kovacs suggests is problematic, since you keep accumulating unreleased hooks if the application creates and destroys styled controls.
- 1
-
1 hour ago, balabuev said: I can reproduce the bug even with this your workaround:
Could you tell us how?
-
I did a bit of research to assess whether other parts of Vcl could be suffering from the same issue. The only controls that recreate themselves in response to the CM_STYLECHANGED message are the TListView and the ActionManager controls (ActionToolbar etc.). I think there are not scrollbars and the like there, but I wonder why there is a need to do that. So the issue may be confined to the ListView and if the call to RecreateWnd can be avoided that would be the simplest solution.
-
Update: This does not work well.
One suggestion I have is the following:
Change TStyleEngine.DoRemoveControl(Control: TWinControl) from:
class procedure TStyleEngine.DoRemoveControl(Control: TWinControl); var I: Integer; LControl: TControl; begin if (FControls <> nil) and FControls.ContainsKey(Control) then begin for I := 0 to Control.ControlCount - 1 do begin LControl := Control.Controls[I]; if LControl is TWinControl then DoRemoveControl(TWinControl(LControl)); end; FControls.Items[Control].Free; FControls.Remove(Control); end; end;
to
class procedure TStyleEngine.DoRemoveControl(Control: TWinControl); var I: Integer; LControl: TControl; begin if not (csRecreating in Control.ControlState) and (FControls <> nil) and FControls.ContainsKey(Control) then begin for I := 0 to Control.ControlCount - 1 do begin LControl := Control.Controls[I]; if LControl is TWinControl then DoRemoveControl(TWinControl(LControl)); end; FControls.Items[Control].Free; FControls.Remove(Control); end; end;
or even to:
class procedure TStyleEngine.DoRemoveControl(Control: TWinControl); begin if not (csRecreating in Control.ControlState) and (FControls <> nil) and FControls.ContainsKey(Control) then begin FControls.Items[Control].Free; FControls.Remove(Control); end; end;
There seems to be no reason to destroy the hook if the control is recreating. Also hooks of child controls would be removed when they get destroyed, so there is no need to do this in advance.
Do you see anything wrong with the above? With the above change the test app runs fine and without memory leaks. And the change would save a lot of hook destruction and reconstruction.
To test copy Vcl.Styles.pas, StyleAPI.inc and StyleUtils.inc to the project source directory, make the change and re-build the project.
Poor performance of Python script
in Python4Delphi
Posted
Does
Output := MainModule.calc_outflow(Random * 10.0);
work?