Jump to content

Kas Ob.

Members
  • Content Count

    464
  • Joined

  • Last visited

  • Days Won

    8

Everything posted by Kas Ob.

  1. Alas, that is the case, and this is why when performance is the target i don't use any managed type, a small record with size, count and a pointer where the item[0] are reference to raw record, with a record helper to provide addition and deteletion. All memory manager i know including system Heap ones, do 16 bytes aligning, the problem in outdated and inefficient RTL managed types design, these should be updated, no memory manager can help with unaligned structure to begin with.
  2. Well that is possible, but it might be an unneeded extra work. What i meant is something like this Just include the batch file which can have relative path, also it can take a parameter for the output path, even the name of the output file. and from the dproj file <PropertyGroup Condition="'$(Base)'!=''"> <PostBuildEvent><![CDATA[BuildConst.bat $(PostBuildEvent)]]></PostBuildEvent> Also i did always prefer using the inc files in place for naming and versioning, over batch files, thus i have their content in the project and in the EXE description ...
  3. True for almost all cases but not all, there still some few edge cases. Another point, it will works for old and new CPU. Another point, it will help any new optimized memory copy functions to use the SIMD version without going through the first and unaligned 16 bytes, but for this it should be aligned to 16bytes to get the most of juice, meaning that record should be 32bytes.
  4. Something like this set filename=MyAppConsts.pas @echo on echo building %filename% @echo off (echo unit Unit1;& echo.& echo interface& echo.& echo const) > %filename% echo APPLICATION_APP_NAME = 'MyApp';>> %filename% echo APPLICATION_COMPANY_NAME = 'MyCompany';>> %filename% (echo.& echo implementation& echo.& echo end.) >> %filename% @echo on @echo done building %filename% #exit 0 the result a file "MyAppConsts.pas" unit Unit1; interface const APPLICATION_APP_NAME = 'MyApp'; APPLICATION_COMPANY_NAME = 'MyCompany'; implementation end. there is no earlier than that !
  5. Another thing, having a record with 17 bytes is little strange, and means you are using packed record, if you are tying to save some performance and minimize time used in copying, then let me assure you are doing the opposite. Remove the packed and/or make sure it is padded to multiple of 4 bytes, meaning 20 bytes should be your minimum, you might see better a little performance right away, even if it is very small it is better, or remove the packed modifier from the record and let the compiler align it for you, unless you are using and exchanging the same records between 32bit and 64bit version, in that case you see how the compiler did it, and replicate the padding yourself with the packed one.
  6. Even when that is the case, i mean allocate new and copy the content, FastMM has very nice algorithm to recycle the system allocated blocks, FastMM request and allocate chunks from OS, lets say we have 1 MB allocated, FastMM will slice it into smaller chunks and provide the application with requested sizes, when a requested size is not available, it will request another big check 1MB and repeat, the probability of you cause fragmentation is very low, as it only will happen with the 1MB(s) chunks, and to do this you will have to requested and not freed too many blocks (huge amount of small sizes) , then free have of them in specific order to cause, but when your lets say list will grow above certain size then FastMM will not utilize the sliced chunks, it will request new form the OS and free (return it to OS) it when done, hence no matter how you changed or resize your list or even tens/hundreds of them you will not be able to cause any memory fragmentation, nor memory access degradation, the performance will more or less stay the same. Relocating on single delete as Stefan pointed is a problem, so do as Dalija suggested or implement you own raw list which will perform relocate on delete, it is easy. One more thing, many thinks linked lists are good and fast, and they are but only when the items count reach something around 500k item. Another thing, if your list doesn't need to be ordered, then on delete move/copy the last item on top the deleted one and decrease the count, hence remove few overheads, in such case your list will need size (capacity) and count (actual items count), grow and resize only when count is equal to size, and never downsize !, the logic is simple here, this size needed once so it could be needed again.
  7. Don't worry there, as long you are using FastMM (or the default memory manager, they are the same) , there is no memory fragmentation, in fact it is impossible to fragment the memory when using FastMM with one allocation, these lists/arrays... are one allocation from the point of view of MM, hence however you operate on them like delete and append, it always one continuous allocation.
  8. You still can (and may be should) use the record variant as Stefan suggested, only extended it a little from case boolean to case integer with 0,1 and 2 where you can align the needed types, in the third (additional) case just put case Integer of 0: ( FRandomness0 : byte; FRandomness1 : byte; FRandomness2 : byte; FRandomness3 : byte; FRandomness4 : byte; FRandomness5 : byte; FRandomness6 : byte; FRandomness7 : byte; FRandomness8 : byte; FRandomness9 : byte); 1: ( FRandomness0_1 : Word; FRandomness2_9 : UInt64) 2: ( FRandomness0_N : NativeUInt); There is no need for padding, the compiler should take care of that.
  9. This made no discernable difference. It is expected with your modern CPU, but the difference will be noticeable when the CPU under high usage, this is just for information and you don't need to do it, i liked to share. Anyway, on my XE8 i found one of these loch ness monsters, the one that i saw in real life and in dreams, the ones that very hard to catch red handed, when the compiler throw the towel and silently produce unoptimized ugly assembly instructions. This is reproducible on my XE8 and would love to know if this case is still in newer versions: 1) create new and empty console applicaiton. 2) add VSoft.Ulid 3) put this line in the main procedure, uses System.SysUtils, VSoft.Ulid; begin TUlid.Create; // we don't care now for memory leak or whatevery end. 4) add 64-bit platform and enable optimize, keep stack frame enabled. 5) use this create, the same i used earlier class function TUlid.InternalNewUlid(timestamp: UInt64): TUlid; var random : UInt64; ts : Int64Rec absolute timestamp; begin result := default(TUlId); //reverse order! result.FTimeStamp0 := ts.Bytes[5]; result.FTimeStamp1 := ts.Bytes[4]; result.FTimeStamp2 := ts.Bytes[3]; result.FTimeStamp3 := ts.Bytes[2]; result.FTimeStamp4 := ts.Bytes[1]; result.FTimeStamp5 := ts.Bytes[0]; random := FXorShift64.Next; PNativeUInt(@result.FRandomness0)^ := NativeUInt(random); random := FXorShift64.Next; Move(random,result.FRandomness2, 8); // comment and uncomment this line and see the difference in assembly code PUInt64(@result.FRandomness0)^ := random; end; The result is astonishing ! Here the difference from my XE8 The compiler with move behaved differently and stupidly. One side note this is also really annoying, though i don't know if this still the same for the newer and enhanced versions it could be handled way better for these limited size like up to 128bit.
  10. Kas Ob.

    Looking for some guidance on WM_NCCALCSIZE

    One more thing that bothering me, and you might test and confirm as you or somebody have some insight on this: Do the borders scale ? I mean the border is there for reason and they are one pixel wide, for the window title, but when scaled to 200% do they become 2 pixel ? or they stayed as 1pixel? See, windows shadow bothered me in the past and they changed few times over many Windows versions, their scaling too.
  11. Kas Ob.

    Looking for some guidance on WM_NCCALCSIZE

    I still thinking about this, and as no one added any input then i add my assumption. I think the logic behind this is very simple, Windows has its default drawing (themed or not) and if you are changing its parameters in whole or in some then you are on your own. In examples might be easier to explain, like, while you change the height of an item in ListBox or ListView, then the default drawing/rendering for item will perform according to its default no matter what you have set, if you set the item height to 3 pixel then you will see the item drawn by OS stayed as default like 8 pixel and the items will overlap, same if you set them to something like 50 pixel, the system will not check and adjust the font size neither the image or checkboxes... Its like an unspoken rule, if you are changing the height then you have to continue and override the default drawing, and draw your own title/item.. with your own drawing function and your own font, brush, pen....
  12. Here another idea. The implementation require 2 consequence random then calculate them in one hit, instead of calling FXorShift64.Next in the middle, in other words add/use Random1 and Random2, use only one Next.
  13. @Vincent Parrett I would suggest to benchmark the following class function TUlid.InternalNewUlid(timestamp: UInt64): TUlid; var random : UInt64; ts : Int64Rec absolute timestamp; begin result := default(TUlId); //reverse order! result.FTimeStamp0 := ts.Bytes[5]; result.FTimeStamp1 := ts.Bytes[4]; result.FTimeStamp2 := ts.Bytes[3]; result.FTimeStamp3 := ts.Bytes[2]; result.FTimeStamp4 := ts.Bytes[1]; result.FTimeStamp5 := ts.Bytes[0]; random := FXorShift64.Next; //Move(random,result.FRandomness0, 2); // randomness 0-1 //PWORD(@result.FRandomness0)^ := UInt16(random); // the exact size PNativeUInt(@result.FRandomness0)^ := NativeUInt(random); // it is safe to overflow here, might yeild better and simpler asm instruction random := FXorShift64.Next; //Move(random,result.FRandomness2, 8); // randomness 2-9 PUInt64(@result.FRandomness0)^ := random; end; Removing the overhead of Move should count for something, i guess !!
  14. Kas Ob.

    Looking for some guidance on WM_NCCALCSIZE

    Searching the Internet kept landing me on the same approach, using "Self.Height - ClientHeight" the result was 39 pixel, and using print screen and painter to find the title bar height kept giving me 29 pixel, so some subtraction will be needed from the first, which i think it borders.... Anyway i found this https://stackoverflow.com/questions/28524463/how-to-get-the-default-caption-bar-height-of-a-window-in-windows Which is does return 31 pixel, so it might be your solution, or at least it a lead to a solution. Please share your finding with us.
  15. Kas Ob.

    Looking for some guidance on WM_NCCALCSIZE

    Have you tried WM_GETTITLEBARINFOEX ? Its message structure not available in XE8, so i can't try it, but seeing this : https://stackoverflow.com/questions/60667679/delphi-overlap-window-form-titlebar then TTitleBarInfoEx should be available in newer versions.
  16. Well, first what comes to mind is MQTT, there is a punch of free and commercial libraries for Delphi/Pascal that provide MQTT. about how to handle no DNS, well if you buy a DNS form anywhere then move it to your free CloudFlare account, then you have very powerful infrastructure to build on, see, you don't need to assign your bought DNS to the server IP, you could use a subdomain, also you could use a subdomain TXT record, the client in this case will not lookup lets say myserver.alank2.com, but will the TXT record for this subdomain (or any domain) where the server IP is stored as plain text value, then connect to it. You can borrow a subdomain form i friend, a trusted one to store your TXT record. CloudFlare has brilliant control over DNS and its records, the slowest changes i witnessed was around 45 seconds over so many years of using their DNS services and thousands of changes and update, (many of these changes were automated using their API), using free CL account. Also just searched for MQTT providers in case you don't want to use your own server at all, and found this https://myqtthub.com/en They looks like have free account to test, it might be enough for you.
  17. Well, as Francois said it, and i will elaborate a little on that. TCanvas is Delphi/Pascal RTL encapsulation for Device Context: https://learn.microsoft.com/en-us/windows/win32/gdi/device-contexts https://learn.microsoft.com/en-us/windows/win32/gdi/about-device-contexts These are Output devices and has no Input, and by Input i mean there is no hardware user input or interaction, their input is solemnly comes from drawing APIs, and in other words they are GDI drawing object that simulate a painting canvas (real life canvas) to be processed further, like to be rendered on monitor or to be passed to printer.... they are build that way to unify User mode GDI and supply one Interface to different hardware, their functionalities differ based on what the hardware and its driver support, while the interface is the same for them all.
  18. Kas Ob.

    Avoid parameter evaluation

    Can't shake it off without trying to validate the type, so here what i tried and it is working perfectly {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; procedure DoProcess(const Args: array of const); var i, Count: Integer; begin Count := Length(Args); if Count > 0 then for i := 0 to Pred(Count) do begin case Integer(Args[i].VType) of vtString, vtChar, vtWideChar, vtAnsiString, vtWideString, vtUnicodeString: Writeln(string(Args[i].VString)); vtInteger: Writeln(Args[i].VInteger); vtInt64: Writeln(Int64(Args[i].VInt64)); else Writeln('Type handling not implemented yet ' + IntToStr(Args[i].VType)); end; end; end; procedure Test; var st: string; begin st := 'Param1'; DoProcess([st, 'Param2', 5]); end; begin Test; Readln; end. output The generated assembly is nice and short, one thing though, don't forget to declare Args itself as const as in procedure DoProcess(const Args: array of const) or it will be copied locally, in other words it will be ugly.
  19. Kas Ob.

    Avoid parameter evaluation

    I never thought about this. Thank you !
  20. Kas Ob.

    Avoid parameter evaluation

    Well my point was about to not call SysErrorMessage in the first place, just pass the GetLastError, even more on this, if the code between Log and where actually it implemented can't modify The GetLastError, then you could use ttTraceIWithError and there you can call SysErrorMessage. In case you care about performance then just use a method and track the generated assembly And by replacing the most repeated or even the specific phrases like 'Test' and 'Exception'... with LOG_String_XX you can short and fast logging. Again food for thoughts.
  21. Kas Ob.

    Avoid parameter evaluation

    There is few ways i can think of like these 2 approaches type TLogLevel = (llInfo, llTrace, llError); procedure Log(LogLevel: TLogLevel; const LogText: string); overload; begin end; procedure Log(LogLevel: TLogLevel; const Text: array of string); overload; var Count: Integer; begin Count := Length(Text); case LogLevel of llInfo: ; llTrace: begin if Count > 0 then begin end; end; llError: ; end; end; procedure Log(LogLevel: TLogLevel; const LogText: string = ''; const Error: string = ''; const Additional: string = ''); overload; begin end; procedure Log(LogLevel: TLogLevel; const LogText: string = ''; ErrorCode: Integer = 0; const Additional: string = ''); overload; begin end; procedure TForm10.FormCreate(Sender: TObject); var BuildStringFromContext: string; begin Log(llTrace, ['Test', IntToStr(GetLastError), BuildStringFromContext]); Log(llError, 'Test', GetLastError, BuildStringFromContext); Log(llError, 'Simple Text', 0); Log(llInfo, 'Simple Text', 0, 'Additional Info'); Log(llInfo, 'Simple Text', ''); end; Just food for thoughts for you, using array of string is easy and will make you life easier, but you should remember the order, while the overloaded versions with default empty values, are more readable and harder to confuse. Each had its own shortcoming, like i had to add 0 do the ambiguity, but if you make you mind you can build them right and remove the need for these extra 0 or ''. It is up to your need to think of a solution that you are comfortable with.
  22. Yes , and to be more accurate explaining this case with both addresses are the same, The EIP register pointing to a page-protected address, means the CPU is already try to fitch an instruction while the virtual mode triggered a page fault, hence the same address of the execution and accessing, this happens with few cases, like the stack is corrupted and a ret instruction caused the CPU to pick an invalid address to return to, or a hook or resolved API addresses pointing to a library, and that library has been already freed and its memory returned to the OS where marked again as protected or not allocated. Not long ago there was similar bug discussed in the German forum comes from broken hooking that corrupt the stack after calling the hook, sorry can't find it now ! may be someone can help with that. @christos papapostolou i would suggest to check and refine your uses clauses and find any exotic units or library that are outdated, also try to capture the stack list, it will help.
  23. Frankly speaking, this part is quite unexpected for me, since I never had a chance to dive deep into RTTI, and the “classical” Pascal treated types T1 and T2 equal whenever there ould be a T1 = T2 declaration. I think I understand the reasons, but I’ll have to read more on topic to be sure. Well, hold your horse here for minute. I might made a mistake here, or did i ? Things seems a little different between Delphi 2010 and XE8, so it might be different for you with your version. My mistake is between these type TSomeType = MyTypes.TSomeType; type TSomeType = type MyTypes.TSomeType; the latter does have different RTTI for sure, but for the first is does not, and that is my mistake, yet helpers is confused as hell between the two type and my two already old Delphi versions compiler, one allow it and the other is buggy and confuse them, and from there my mistake did come. i rarely used similar approach for such renaming or like this var LDefObject: TObject; LObject: TMyObject; LMyObjectEx: TMyObjectEx absolute LObject; begin Memo1.Lines.Add(LDefObject.HelperName); Memo1.Lines.Add(LObject.HelperName); Memo1.Lines.Add(LMyObjectEx.HelperName); To have my own string helper with casting and without losing the default string helper, and i don't know if this is working on the newer versions, yet now i trying to add a helper for string on my Delphi 2010 and it does allow it
  24. Hi, Few thoughts on this: 1) If MyTypes is used through out the project then no point of renaming or redefining its types in other units, it will be there and everywhere. 2) From what i understand, UnitB is to somehow can be named as UnitAEx or UnitAHighLevel, this will draw clear path of using and remind of adding UnitA where it extended version is used, the one (not/ex) called UnitB. 3) About this I don't think i ever used such renaming easily as it is easily can tangle things and cause more and different problem in the future, the one you missed now, see TSomeType on the left is a rename or redefine of MyTpes.TSomeType, yes they are the same, but in you view not for the compiler, example RTTI is different for them and they are not equal ! also in some places you will see the compiler might complain about the definition, usage and passing them as parameters .... also it might broke things badly in case such units used in an Design Time package. I see if that is needed then use it where is the impact is smallest, like in UnitB to rename types from UnitA instead of redefining types from MyType unit into UnitA, try to narrow the scope in it is needed. Now to the questions I am no aware of such best practice if it is exist, i follow best practice and simple and short logic, keep it stupid simple, and also untangled as much as possible. OK it is, yet it depends on the case and what could be changed in the future, rarely one can see that today, i made so many mistakes, not real mistakes as this is not a mistake per se, it is just short sighting and not leaving doors open but .... not tangle many things together. No sure i do understand the question, but i hope my points above could give you some insights. The above is my personal opinion, and i am sure many in the forum can have helpful insights on this.
  25. Kas Ob.

    TControlList — need help!

    @#ifdef Well , while i hate my life nowadays, and while just waiting for the blackout to brighten my day after only 3 hours of power, i tried to build you something Far from finished or polished, take it as example or prototype for what you can do with custom drawing, notice it is 10000 strings ! ListBoxAsStringList.zip My suggestion is to use VirtualTreeView, i couldn't find working copy of VTV on my PC now, and don't want to waste time downloading my encrypted archives from the cloud, so i used TListBox, it is simple easy and pretty straight forward. Notes: 1) i didn't use FillRec on the whole Listbox, while it is more efficient and faster to clear the background for all the items in one go, this will introduce an ugly flicker in the bottom half, do the system rendering which doesn't wait for anyone, by clearing the background for each item in time, we prevent this flicker, but will introduce small performance hit in overall drawing. 2) i disabled the selected item highlighting, because it does need little different approach, yet it is easy, just adjust the canvas brush color before the FillRect, this will be up to you to implement, as i said i don't have time and don't have the skins on the latest IDE versions to test and play with. 3) customize as you wish, like ... can you this ? ( there is no noticeable performance change) Hope that helps, also never underestimate Windows custom drawing, it is fucking blazing fast when done right.
×