-
Content Count
1428 -
Joined
-
Last visited
-
Days Won
141
Everything posted by Stefan Glienke
-
MAP2PDB - Profiling with VTune
Stefan Glienke replied to Anders Melander's topic in Delphi Third-Party
Recompile the RTL yourself using source\rtl\buildrtl.bat and generate map file - that's how I used to do it until Anders added jdbg support. -
Recursive anonymous functions
Stefan Glienke replied to Primož Gabrijelčič's topic in Algorithms, Data Structures and Class Design
There is no local variable - on the source code level, yes there is, but implementation wise they live inside the heap memory of the compiler generated instance. That's why captured variables never appeared in the local variables view until recently when the kinda implemented support for it. -
Watch me coding in Delphi on YouTube
Stefan Glienke replied to silvercoder79's topic in Tips / Blogs / Tutorials / Videos
omg, no! Followed by "How do I fix these random Access Violations that appear in my code" -
Recursive anonymous functions
Stefan Glienke replied to Primož Gabrijelčič's topic in Algorithms, Data Structures and Class Design
To understand this, you need to understand how anonymous methods are implemented. The compiler implements them by creating a class that inherits from TInterfacedObject. The anonymous method type is a special interface with one method called Invoke, which has the signature of the anonymous method type. When you add ReportMemoryLeaksOnShutdown := True to the code above you will see that it reports one instance of a class called MakeFib$ActRec - that is the class name of the compiler-generated class for the anonymous method. The class also contains fields for every captured variable - in this case the local variable fib is being captured with holds the reference to itself so this is the circular interface reference that causes the memory leak. To demonstrate here is the code as the compiler implements it: type MakeFibAnonClass = class(TInterfacedObject, TFunc<integer,int64>) fib: TFunc<integer,int64>; function Invoke(value: integer): int64; end; function MakeFibAnonClass.Invoke(value: integer): int64; begin if value < 3 then Result := 1 else Result := fib(value - 1) + fib(value -2); end; function MakeFib_Impl: TFunc<integer,int64>; var obj: MakeFibAnonClass; begin obj := MakeFibAnonClass.Create; obj.fib := obj; Result := obj.fib; end; -
Methods to convert a string TValue to the desired simple type (Like Integer, Float, DateTime);
Stefan Glienke replied to dmitrybv's topic in RTL and Delphi Object Pascal
Convert<T> from TValueHelper in Spring. See https://bitbucket.org/sglienke/spring4d/src/2.0.1/Source/Base/Spring.pas#lines-651 -
get the source from SourceForge open Projects\DelphiXx120\GExpertsRS120.dproj compile run Binaries\Register-GExperts-XX120.cmd restart IDE
-
updated Delphi64RTL intel ipp onetbb
Stefan Glienke replied to RDP1974's topic in RTL and Delphi Object Pascal
You *must not* remove that line - Move in Delphi allowed to be called with negative Count (a possible result of some size calculation), resulting in a no-op in the System implementation. Passing a negative number to most C++ implementations will result in passing a value >2mio because their size parameter is unsigned. Also, the performance difference is hardly about that little check but the ippsMove_8u implementation. -
updated Delphi64RTL intel ipp onetbb
Stefan Glienke replied to RDP1974's topic in RTL and Delphi Object Pascal
ippMove is not much faster than System.Move in Delphi 12. In fact, it performs noticeably worse on small sizes (below 1k) and for some reason completely falls off the cliff on sizes that are >20k running 2-4 times slower. -
Dynamic array used as a queue. Memory fragmentation?
Stefan Glienke replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
Only on 64bit though - on 32bit the size of TDynArrayRec is 8 byte, if the memory manager getmem returns 16byte aligned memory that makes the elem[0] 8byte aligned. This is why i said that the RTL has to take care of it - the same is the case for strings. Relying only on the memory manager which is replaceable prevents the RTL from doing things that it could if certain data would have certain alignments. Using SSE for some string handling functions and such. -
Dynamic array used as a queue. Memory fragmentation?
Stefan Glienke replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
That would be true if dynamic array allocation would be done in a way that ensures that element [0] would be at a 16 byte aligned address, which is not the case -
Dynamic array used as a queue. Memory fragmentation?
Stefan Glienke replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
Not exactly, it moves everything in the array to the front and then calls SetLength with the new length. What that does internally depends on the refcount of the array. If it's 1, then it does a Realloc (shrinking in place, though depends on the memory manager what exactly happens, typically for a small downsize it does it in place, thus basically resulting in a no op) If it's greater than 1 it allocates a new block and copies the content over - I would say calling SetLength inside of Delete is a bug because everything was already moved in place thus the behavior of SetLength is not to be desired in this case. In fact this leads to some corrupted copy lying around Reported https://embt.atlassian.net/servicedesk/customer/portal/1/RSS-1532 Edit: apparently this is already known since 2020 - https://quality.embarcadero.com/browse/RSP-27870 -
Devin AI - Is it already happening?
Stefan Glienke replied to FreeDelphiPascal's topic in General Help
Because things like Clean room design exist to minimize the chances of any copyright infringement. There have been lawsuits over "similarly enough looking" code in the past. Also, the simple fact that gen AI does not care about any license attribution which you would have to follow if you take any source code with one of the major permissive licenses that still require attribution. -
Devin AI - Is it already happening?
Stefan Glienke replied to FreeDelphiPascal's topic in General Help
It's really embarrassing to see how many people who work in software development don't know how generative AI works and are being fooled by it like an average TikTok teenager. -
Devin AI - Is it already happening?
Stefan Glienke replied to FreeDelphiPascal's topic in General Help
-
This and also a kind of limited approach given that the lifetime of all those UI elements is bound to the application lifetime and they are all singletons (meaning that you cannot have more than one instance of say ITestDlg)
-
Devin AI - Is it already happening?
Stefan Glienke replied to FreeDelphiPascal's topic in General Help
The last thing I know of is that Devin AI was fake (that info is from April 2024 when the entire developer community was all over the demo they showcased). Do you know more? -
how to filter on source files using VTune?
Stefan Glienke replied to merijnb's topic in Delphi Third-Party
Changing anything with the map file will only remove the names, the functions being called will still be in the sampling result because VTune is a sampling profiler. Given that obviously, your performance problem was not with these functions they should not be a significant part of the result percentage-wise - if they are, they are part of the issue. -
Unicode NBSP(u00A0) No-Break Space character for 64-bit
Stefan Glienke replied to sp0987's topic in RTL and Delphi Object Pascal
Can't be asked - I am using the sfw (safe for work) version 😇 -
Unicode NBSP(u00A0) No-Break Space character for 64-bit
Stefan Glienke replied to sp0987's topic in RTL and Delphi Object Pascal
FWIW the assembly implementation of that function is pointless given you can do it in a way more readable way - also performance cannot be a reason given the following call to mem_write causes a temporary heap allocation to convert your hex_code variable into an AnsiString. Quickly slapped together (anyone who wants to further optimize this - be my guest, i cba right now): type hex_code = Array [1 .. 4] of AnsiChar; function Int2Hex(c: Word): hex_code; const HexChars: array[0..15] of AnsiChar = '0123456789ABCDEF'; var i: NativeUInt; begin i := c; Result[4] := HexChars[i and $0F]; i := i shr 4; Result[3] := HexChars[i and $0F]; i := i shr 4; Result[2] := HexChars[i and $0F]; i := i shr 4; Result[1] := HexChars[i and $0F]; end; and then instead of mem_write with AnsiString as the first argument use one that writes 4 bytes at once. -
Unicode NBSP(u00A0) No-Break Space character for 64-bit
Stefan Glienke replied to sp0987's topic in RTL and Delphi Object Pascal
Your assembly code of Int2Hex for 64bit is wrong - c is passed in RCX. -
Don't freak out! It's just a bug: Debugging with Delphi
Stefan Glienke replied to silvercoder79's topic in Tips / Blogs / Tutorials / Videos
Nitpick at 10:30 - strings are always empty when not explicitly initialized like all managed types. -
Please see the first entry that Google provides when searching for "Addictive Software" When looking for spell-checking alternatives - there is also this thread:
-
I can absolutely repro - all my 20 logical cores (i5-13600k) go to 100% for 10 seconds. Running it through SamplingProfiler right now to check. Edit: Okay, either this has regressed at some point after the demo was originally built or it was overlooked that there is actually no real workload inside of the delegate and thus it just measures the huge overhead from RTL and interlocked operations. It's just spending a huge amount of time wrapping and unwrapping the integer from TOmniValue and sharing the counter across all threads causing a complete bus lock every time due to the usage of DSiInterlockedExchangeAdd64 (*). (*) I wrote bus lock and this is not entirely true, so before anyone chimes in quoting the Intel manual about the fact that the lock prefix does not automatically cause a bus lock - you are correct. Here we have the case that we are sharing the one variable across all the cores we have so it has to travel back and forth the CPU caches to and from RAM. This code as is would be a nice worst-case example for Primoz' book about what can potentially go wrong when doing parallel programming. However: keep in mind that we don't have any real workload which would most likely change the picture as the workload usually takes the majority of processing time and not the parallel foreach code. P.S. Among the aforementioned things it looks like the OTL code (particularly TOmniValue) is suffering from some overhead caused by inlining. For example: because TOmniValue.AsInt64 as well as TOmniValue.TryCastToInt64 is inlined it causes the temporary Variant variable it needs for the otvVariant case to be spilled into the location where the inlining happens. But in our case we never have to even deal with a Variant hence all the extra code the compiler produces is just overhead. And because the getter for AsInt64 is used twice, the compiler repeats the inlined code twice and produces two Variant variables which it finalizes using System.FinalizeArray. Also a lot of record initialization and finalization is happening which I assume (did not look closer) is being caused by TOmniValue - potentially also because of some temporary compiler generated variables. Here is the drilldown of the interesting pieces when running in SamplingProfiler:
-
No, you can even see the stackframe settings in System.pas because it explicitly specifies them at the very beginning. Furthermore, even without explicitly enabling them, the RTL is being compiled with $W- It's a compiler implementation that it enables stackframe for any function that uses ReturnAddress - you can double-check that for yourself by compiling the following code and looking at the disassembly for Foo: {$STACKFRAMES OFF} procedure Bar(p: Pointer); begin end; procedure Foo; begin Bar(returnAddress); end; begin Foo; end. I know this because I was using ReturnAddress in my code and it behaved wrong in XE where it was implemented explicitly as I linked in my post above but without stackframe, this returned a wrong address. This is why I explicitly enable stackframes for the code that uses this function in XE - see https://bitbucket.org/sglienke/spring4d/src/2dbce92195d699d51fc99dd226c4698748ec8ef9/Source/Base/Spring.pas#lines-3474
-
XE2 - see https://bitbucket.org/sglienke/spring4d/src/2dbce92195d699d51fc99dd226c4698748ec8ef9/Source/Base/Spring.pas#lines-3140 Anyhow the ReturnAddress function would not help here because it would point at the location in System._AbstractError which is the function that calls AbstractErrorProc. This improved handler is a highly brittle hack that might work depending on your compiler settings. It does not force specific compiler settings for the u_dzAbstractHandler.pas unit which also changes where the return address 2 calls up is to be found and it also does not even work for x64. I also don't share Thomas' assessment that this change came in Tokyo but I rather suspect that his stackframe settings were different between his tries on different Delphi versions. The code that is responsible for the abstract error did not change between Delphi XE (the oldest version I can check right now) and 12. The best way would probably be to use Caller(2) from JclDebug which does a proper stack walking to determine the return address.