-
Content Count
1428 -
Joined
-
Last visited
-
Days Won
141
Everything posted by Stefan Glienke
-
Caching with class variables
Stefan Glienke replied to Primož Gabrijelčič's topic in Tips / Blogs / Tutorials / Videos
The approach to store Min and Max Value for sets does not work as sets in Delphi can be max size of 32 Byte (set of Byte) - good luck storing that in an Integer. The core idea though is nice - I think I have also done this before: use a generic type as storage for type related values. -
appending to a dynamic array
Stefan Glienke replied to dummzeuch's topic in Algorithms, Data Structures and Class Design
Technically in Delphi strings are only mutable if they have a ref-count of 1, otherwise a copy happens. And while it might be convenient to the average developer if you actually look under the hood how much instructions and implicit heap allocations happen if you are not paying attention and naively work with strings you will be scared. -
- the december patch does not mention anything about IDE stability fixes - IDE FixPack is not yet released and only alpha state (and even may cause issues - well more precisely the CompilerFixPack for 64bit windows does - see https://quality.embarcadero.com/browse/RSP-23405) - DDevExtensions does not fix IDE stability issues but adds productivity features I am only using 10.3 for toying and it more often crashes (well the compiler gets into some state where it produces nothing than some internal errors after it eventually brings down the IDE) - and yes I am using the IDEFixPack alpha because I am not producing any production software with it.
-
appending to a dynamic array
Stefan Glienke replied to dummzeuch's topic in Algorithms, Data Structures and Class Design
https://stackoverflow.com/questions/22397861/why-is-string-immutable-in-java https://stackoverflow.com/questions/2365272/why-net-string-is-immutable -
Allocation-Free Collections
Stefan Glienke replied to Erik@Grijjy's topic in Tips / Blogs / Tutorials / Videos
As I mentioned before the jump optimization highly depends on the CPU - and of course it only affects methods that are being called and not inlined. When inlined you have a jump no matter what. Only when being called it can benefit from two returns in each branch to avoid the jump over. Edit: Clarification - when inlined, putting the raise first is better because it only needs one conditional jump instruction (jump over raise call or not), taken in the common case. When putting it below it still has the conditional jump which is not taken in the common case but has a jump over this code in all cases when the common path is being used. What I would do regardless is to put the exception raising code into a non inlined subroutine to reduce generated binary code because then the code being jumped over is just a call (5bytes on 32bit). Another optimization is to make use of {$POINTERMATH ON} for your pointer type because then you can write: Target := @FData; Target[FCount] := AItem; And another neat trick to check if an Integer index is in range that only uses one compare: if Cardinal(AIndex) >= Cardinal(FCount) then By the way - putting all cases into the same binary often influences results and even the order in which you execute them changes the output - I had a similar case where just changing order made one or another faster or slower. Measuring performance is not as easy at it sounds :) -
Allocation-Free Collections
Stefan Glienke replied to Erik@Grijjy's topic in Tips / Blogs / Tutorials / Videos
Any pure speed benchmark would be rather useless/biased because it all depends on what you are in fact doing apart from creating the list. If this workload is small enough the object creation would mean a significant overhead. In the end you would just benchmark object creation. Also what affects performance is pointer indirection and memory, CPU cache. In some micro benchmark this would not occur as all memory fits into L2 or even L1 cache. Benchmarking the effect on heap usage/fragmentation is way harder because that cannot be done in pure isolation and usually only manifests in production. FWIW performance for System.Generics.Collections.TList<T>.Add (and other methods) between 10.2 and 10.3 also differs quite significantly (got better). And another thing that sometimes significantly improves performance when adding items to a list is to preset its Capacity to avoid reallocations - in the example given in the blog article with adding 64 integers this already improves performance by 33%. -
appending to a dynamic array
Stefan Glienke replied to dummzeuch's topic in Algorithms, Data Structures and Class Design
Turn ZBS off for that code and use 1. -
Allocation-Free Collections
Stefan Glienke replied to Erik@Grijjy's topic in Tips / Blogs / Tutorials / Videos
Yes, and performance of these things sometimes heavily differs between the different CPUs (for example I had a case where I got some severe slowdown in some code on my ivy bridge i5 whereas it was not there on a skylake i7 one). Anyhow not having a jump is still better than a predicted one. But that is micro optimization (which I did invest some time into as the code I was testing is core framework code, which I usually consider collection types part of). -
Allocation-Free Collections
Stefan Glienke replied to Erik@Grijjy's topic in Tips / Blogs / Tutorials / Videos
No. I don't know about the ARM codegen but on windows if you do the pattern: if out of range then raise exception; do stuff; This always generates a jump above the raise to get to do stuff for the common case of "in range". When you write: if in range then do stuff else raise exception; In this case you don't have a jump for the common case of being in range. Depending on how you structure the code you then have two returns in each branch of the if so no jump inside the method. If you like to inline this method it is also a good thing to put the raise exception into its own subroutine to not bloat the inlined code. -
Allocation-Free Collections
Stefan Glienke replied to Erik@Grijjy's topic in Tips / Blogs / Tutorials / Videos
And if you then write the Add and GetItem methods better so they don't do unnecessary jumps every time and can be inlined it will be even faster. -
appending to a dynamic array
Stefan Glienke replied to dummzeuch's topic in Algorithms, Data Structures and Class Design
What's also interesting about Length and High is that if you are just writing code that directly compiles into a binary they are being inlined by the compiler whereas if you are using them in units that are pre-compiled as part of a package (if you are 3rd party component vendor for example) they are not and always cause a call to System._DynArrayHigh and System._DynArrayLength. Funny enough this is not the case for the source shipped with Delphi because their DCUs are not generated from their containing packages. -
Generic Dictionary and an inline variable: bug or misuse?
Stefan Glienke replied to Kryvich's topic in RTL and Delphi Object Pascal
This bug has already been reported before but since QP is not responding right now I cannot look for it.- 3 replies
-
- tdictionary
- inline variables
-
(and 2 more)
Tagged with:
-
Include unused class and its RTTI in the executable
Stefan Glienke replied to Attila Kovacs's topic in General Help
I don't see the Delphi compiler performing any control flow analysis and eliminate code in any possible future. C++ might do that though (see https://stackoverflow.com/questions/6664471/observable-behaviour-and-compiler-freedom-to-eliminate-transform-pieces-c-co) However I requested adding a way to specifically force linking of types into the binary some while ago - see https://quality.embarcadero.com/browse/RSP-18389 -
Where is the "Save Desktop..." menu item in the Delphi 10.3 Rio IDE?
Stefan Glienke replied to PeterPanettone's topic in Delphi IDE and APIs
And that's why technical terms matter - because it's called parking brake (Feststellbremse in german). -
Where is the "Save Desktop..." menu item in the Delphi 10.3 Rio IDE?
Stefan Glienke replied to PeterPanettone's topic in Delphi IDE and APIs
Well not trunk but in fact Mercedes Benz I think was the first manufacturer to put it somewhere else and often people were looking for it in the usual place and could not find it :) -
Travis CI joins Idera
Stefan Glienke replied to Ugochukwu Mmaduekwe's topic in Tips / Blogs / Tutorials / Videos
Looks like a similar story as with some other acquisitions - bought when decreasing relevance kicked in (in this case I think CircleCI their direct competitor gained quite some marketshare from Travis for several reasons). -
Maintaining For loop(s)
Stefan Glienke replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Thanks for pointing that out - in the currently released version the type still has a T in its name and is in the unit Spring.Designpatterns (the move and renaming happens in the upcoming 1.3 version which is not yet released). Also in both versions the explicit hardcasting of the anonymous method is not required - there is an implicit operator for that. Also FWIW I usually put specifications that are used in several places as static functions into their own type or helper just like this (you can also make them standalone routines if you dislike the redundant typing of TProjectFilters): type TProjectFilters = record class function IsActive: TSpecification<TProject>; static; class function WorkhoursGreaterThan(const value: Integer): TSpecification<TProject>; static; end; class function TProjectFilters.IsActive: TSpecification<TProject>; begin Result := function(const p: TProject): Boolean begin Result := p.Active; end; end; class function TProjectFilters.WorkhoursGreaterThan( const value: Integer): TSpecification<TProject>; begin Result := function(const p: TProject): Boolean begin Result := p.Workhours > value; end; end; var spec: TSpecification<TProject>; begin spec := TProjectFilters.IsActive and TProjectFilters.WorkhoursGreaterThan(50); Also what others already pointed out: lets look at for example CountActiveProjects and ListActiveProjects - what do they have in common? They do something on the active projects - so break that down into getting the active projects and what is done on them. If you want to keep TArray<TProject> and alike you probably have to write the filtering yourself. I just point out how easy it will be using IList<T> from Spring.Collections: procedure PrintProject(const p: TProject); begin //... end; var projects: IList<TProject>; activeProjects: IEnumerable<TProject>; begin activeProjects := projects.Where( function(const p: TProject): Boolean begin Result := p.Active; end); Writeln(activeProjects.Count); activeProjects.ForEach(PrintProject); You can even turn a TArray into an IEnumerable where you can apply filtering and stuff (yes, some minor overhead - measure yourself if it's worth it to keep your code compact and with less explicit loops); var projects: TArray<TProject>; activeProjects: IEnumerable<TProject>; begin activeProjects := TEnumerable.From(projects).Where( function(const p: TProject): Boolean begin Result := p.Active; end); Writeln(activeProjects.Count); activeProjects.ForEach(PrintProject); -
ABI Changes in RAD Studio 10.3
Stefan Glienke replied to David Millington's topic in RTL and Delphi Object Pascal
Well, not quite - it's not a common case but I want to point that out: If you have any existing 64bit DLL that you compiled with a previous version and are now calling from a 10.3 compiled binary or vice versa that has any of the affected record types you are in trouble. You need to compile both with the same version. Also if I am not mistaken it is between 5 and 8 bytes, 4 bytes fit into a register on both and should not be affected. -
Has nothing to do with that - I suspect that the dproj was migrated from a version before 10.3 where someone tested with those options. Since there is no IDEFixPack for 10.3 yet, these options will have no effect.
-
https://andy.jgknet.de/blog/2018/06/ide-fix-pack-6-3-released/
-
Changes in Parallel Library
Stefan Glienke replied to hsvandrew's topic in RTL and Delphi Object Pascal
My point was that countering that there is "uncontrollable behavior" sometimes with showing that there is nothing wrong when firing off a bunch of sleep threads is useless. A thread that does nothing but sleep suspends and gives control back to the OS scheduler which knows not to wake up that thread until time is over. You can see that in the numbers you posted. Also when doing IO heavy stuff you hardly want to put that into CPU threads but use I/O completion ports. -
Second alternative possibly does an unnecessary instruction but the first has a jump in both cases - so it depends. If you are micro-optimizing code (which you usually don't) you might want to keep the common path jump free.
-
Changes in Parallel Library
Stefan Glienke replied to hsvandrew's topic in RTL and Delphi Object Pascal
Because Sleep "suspends the execution of the current thread" (see MSDN) opposed to some "real" work. That might or might not affect significantly the behavior of the threading library itself depending on where it has glitches or not. -
Changes in Parallel Library
Stefan Glienke replied to hsvandrew's topic in RTL and Delphi Object Pascal
No offense but I cannot take people serious that test any threading library code by putting sleep into their thread execution to simulate any real workload... -
I really hate this type of paranoia constructions. What do you think about?
Stefan Glienke replied to Juan C.Cilleruelo's topic in Algorithms, Data Structures and Class Design
@Lars Fosdal Goto where?