Jump to content

Kas Ob.

Members
  • Content Count

    658
  • Joined

  • Last visited

  • Days Won

    9

Everything posted by Kas Ob.

  1. Kas Ob.

    The Case of Delphi Const String Parameters

    Nonsense, then lets stop it here.
  2. Kas Ob.

    The Case of Delphi Const String Parameters

    Critical sections are not required here, just special copy string which should touch RefCount of the string global one, could be a new RTL function.
  3. Kas Ob.

    The Case of Delphi Const String Parameters

    Two threads should not be accessing an integer with interlocked operation, so why it should be alright and correct to read a string by two concurrent threads ? (by accessing i know it will work fine with just reading an integer) If that behaviour is needed (reading string from multiple threads in concurrent) then the developer who wrote the thread code should copy the string first, then handle it, it is not the responsibility of the compiler or the RTL, unless it is documented behaviour.
  4. Kas Ob.

    The Case of Delphi Const String Parameters

    What is thread safety anyway? My question should be ( in better English i hope) 🙂 Are stings thread safe? Also what do you think about this discussion ? is it a right to question the possibility of better strings-type-like by fixing the RTL and the compiler ? even if could lead to break in compatibility with different versions of the compiler and the RTL ?
  5. Kas Ob.

    The Case of Delphi Const String Parameters

    What is built on false is false, so that code is working fine because interlocked operation, but it is not sanctioned as correct code and should not be expected to be right or correct to begin with. So breaking compatibility due the code been built on wrong assumption is not big problem as it can be fixed by the developer by copy the string to local var inside the thread code, as it should be his responsibility not the compiler, the threading model and data integrity are the developer problem (responsibility) and his design not the Delphi compiler. Also i don't think breaking compatibility for the sake performance and fixing unseen leaks/bugs is bad decision, on contrary Delphi broke compatibility once with the transition from AnsiString, many if not all the applications back then suffered from this and even some still till now, but are we now in better place with better Delphi and strings ? Yes we are.
  6. Kas Ob.

    The Case of Delphi Const String Parameters

    I was writing and didn't see your post, the question can be simplified to this "is correct" coming from does work fine, but it is ensured by the language or the documentation ? or it does break the rule of string are not thread safe.
  7. Kas Ob.

    The Case of Delphi Const String Parameters

    Then it can be protected like the local var . But here I think i might be start to understand what are pointing to, i might be wrong though, so please correct me You are referring to such code var GlobalString: string; procedure ThreadProc1(st: string); // could be const begin // we are in a thread and accessing only st (not GlobalString) end; procedure ThreadProc2; // could be const begin // we are in a thread and accessing GlobalString end; begin GlobalString := something.. // StartManyThreadsand and call ThreadProc1 (or ThreadProc2 too) end; So here lets discuss this, For ThreadProc1 1) if ThreadProc1 is changing the content of st then we have a problem even with current behavior and code, and this is wrong doing by the developer as strings are not thread safe. 2) If ThreadProc1 is not changing st, then this will work fine in the current implementation, and here the interlocked operation guarantee the safety of RefCount, but lets delay this to ThreadProc2 For ThreadProc2 1) if ThreadProc1 is changing the content of GlobalString then we have a problem even with current behavior and code, and this is wrong doing by the developer as strings are not thread safe. just like ThreadProc1 2) If ThreadProc1 is not changing GlobalString, then this will work fine in the current implementation, and here the interlocked operation guarantee the safety of RefCount, now lets discuss this behaviour, on one hand removing the interlocked operation will definitly break the compatibility with current, most likely many already exist application out there will be broken right away, but this will lead us to the most important question which is Is this assumption sanctioned by Delphi as language and its documentation, i mean global strings are thread safe ? To my knowledge the answer is no, and would like to be corrected on this. Now if the answer is no then the compiler with new behaviour is not breaking any rules, and the code done by many application is the wrong and should be corrected, (also there is tweak/workaround) can used because we assuming GlobalString is not changed then simply by invoking UniqueString to be used with in thread will solve this, and this new behaviour can be documented and will fix all the broken application with the new behaviour, in other words this should be the developer concern. if the answer is Yes then we need to find better approach to either modify the suggestion in whole or a remedy to this global strings usage, does that make sense to you ?
  8. So we have 15mil pixel, now lets assume simple naive assembly handling this pixel by pixel in a loop, and here the loop should also be assembly, and i agree with Thomas on how this should be done, (i do the same ) one ScanLine per bitmap, the naive assembly wit general instruction set can be 3 cycle at most with the loop (not considering the memory bottle neck here because there will be, hit and miss on cache also fetching), anyway, 45mil cycle might be achieved means that converting on 3Ghz CPU will take less than a second adding the memory access overhead, the same memory overhead will be there with MMX or SIMD, but with these you can do many pixel per cycle ( may be +32). If you want us to have fun then please put some small code that really pinpoint the bottle neck and test for its correction and let us have out fun ! of course if assembly is on table. ps : i don't quite understand the target is it to convert 32bit RGBA to 24bit RGB or for just storing (wiring the image on net) to save space ?
  9. Kas Ob.

    The Case of Delphi Const String Parameters

    Thats not true. I've showed it in previous posts. Also this what i meant by one bad example, by me missing explaining in more details. And thinking of this case where the interlocked operation is a must, is the case is only with global constant strings ? because if yes then we easily can stick to initialize the global to 0 and remove the atomic addition/subtraction by utilizing a very same method, ( if we to drop the memory leak protection if full) By changing the addition/subtraction by 1 we can use an algorithm like this 1) if the RefCount <> -1 then add/sub 1 2) if RefCount = -1 then add/sub 0 ( the RefCount didn't change) the branchless assembly for that is xor eax, eax cmp edi, -1 setne al add eax, eax add eax, -1 And here we need the global constants to be initialized to -1, this can be a separate idea to enhance the performance greatly ! but only if the interlocked operation are there to protect global consts string, which i think it is.
  10. Kas Ob.

    The Case of Delphi Const String Parameters

    Sure. but it was global const, and the locking/unlocking will not have any side effect, because it can't bee free and there is no negating what so ever happing on its RefCount. The exception which was intended to prevent data lose and unpredicted behaviour will be happening when the RefCount goes from -1 to 0, while current code implementation behaviour will free the string when the RefCount goes from from 1 to 0, so in theory if this freeing is already happening (i mean from 1 to 0) on a string that been shared by multiple threads then releasing is occurring and the unpredicted behaviour already does exist, and this either by the bug (short implementation) by the compiler or wrong threading model by the developer, right ? am i missing something ? So having two state of the string is not bad, or may be if the compilers developer can join and discuss this further, because i still can't see how this will badly impact the threading model, sure there will be always a chance that i am missing something, but can that something and its effect be considering as bad side effect, hence start different brain storming to fix it elegantly, in other words where are we with this discussing, is it worthless or we can cross the threshold from it is good/better ? The thing is this suggestion combined few ideas, so it definitely can be separated in at least two ideas and investigated separately, or keep them combined to achieve a fix one and for all.
  11. Kas Ob.

    The Case of Delphi Const String Parameters

    Try,,finally should be there and ownership should be reserved, if the inline variables are correctly implemented by the compiler then it should be able to handle this, i think. Here i would point this, i don't have IDE with inline variables to see how it is handled where try..finally for begin block been added by the compiler, i asked in this forum and no one commented on that and i can't find on the internet any technical details, so i am assuming here. No need for this, we are not doing it everywhere, the LockStr/UnlockStr (inside try..finally ) could be only added where mixed call been used just like the case you mentioned with B,C,B,C,B... If a string been used by multiple threads, then 1) it been locked and protected as whole by critical section or any other synchronizing mechanism, so its content should be accessed by only one thread at a time. 2) Lets assume two threads are using it, changing it and passing it to a function with const parameter, means the content is locked by the suggestion, here lets discuss what current behaviour is, one thread lets say is using it, so either it is assigning from it means it increased the the RefCount or it did tried to alternate it, here the data might be freed, if this happen then we adding this suggestion to prevent this to begin with by an exception that should be raised, simply this is exactly the case we need to detect and prevent with an exception, or the code will just increase the RefCount ( according to the suggestion) without the ability to take ownership of the lock, miss using strings like type in thread should be protected by the developer code like in this case, as it is his broken code not the compiler failure to guarantee consistency of constant, like the developer will have his exception at first test/run/debug code hence he is the one who should change his thread to copy the string before using it (like locally ..) when and where he see fit. The lock will be owned by one function only in one place at a time, i think it should be only when the transition is happening, and thinking about flipping with LockStr/UnlockStr this might be unneeded !! as we established the content should be unchangable. And here lets even expect the worse i mean the suggestion failed or missing critical not discussed here situation, then by changing how the RefCount work, by this suggestion (or any other means i am sure there might be other approachs) , where we can remove the interlocked operation ( removing LOCK instruction) we gained at least 18 cycle and this will always occur in pairs means the direct gain is +36 cycle, i don't know for fact how much a try..finally will cost but i don't expect it to do worse than 10 (worst case scinario) this will happen once, so the gain is substantial, that is one, on other hand passing string most likely it will be handled as string further and in many cases there is either a full try..finally or accessing the content in read only(here we need not to protect), with try..finally we most likely don't need to protect this string per se, only in case we will be passing it further as var to be changed and this will violate the contract as we build on top it is const here, so this might simplify many situations, and rethinking now in your mentioned case with B;C;B;C;B... if it did happen within C then this is not a problem , while if it is in A and due the saved time removing the interlocked operation, we are safe to assume adding the lock/unlock in A only will still faster, as we removed the LOCK operation application wide, here one thing if the case of this multiple switching between these calls is there then we even imagine try..finally for each of them as this function is not built performance wise by the developer and this will contradict his assumption and his code to be performance wise, hence adding the try..finally and lock/unlock is the right and safe action by the compiler, let the developer refactor his code. If we dropped the locking in full ( this whole suggestion), and left the new RefCount in little modified version without the negative behaviour, means with no fix for the memory leak, we still gained huge performance, don't you agree ? I am afraid now we are discussing extra deep details and one wrong example, as it happened in this forum quite many times, the whole idea will be deemed as useless issued by stupidity, as i witnessed quite few times, so i really don't want to lose this thread for the void, and also thank you this discussion.
  12. Right, i missed the word best tool, and what i want to say it is the best to see opened files and their access mode (read/write..) system wide. Not many know about Handle so i was comparing it to other external tools.
  13. Kas Ob.

    Customizing source editor

    Localization or converting into constant...etc , but also strings is marker to see code, so when i am reading code i can collect images in brain on the layout code, or just strings make things more colorful. 🙂 Never thought about why i like them like that.
  14. Kas Ob.

    The Case of Delphi Const String Parameters

    Tricky one indeed ! 🙂 Here in this case if my last suggestion with removing the RefCounting inc/dec and leave the negate, in this case the code will be simplified to this to prevent locking procedure UnLockString(Str); inline; begin // two assembly instruction at most to simple negate or make sure it is positive RefCount end; procedure LockString(Str); inline; begin // two assembly instruction at most to simple negate or make sure it is negative RefCount end; procedure A(st: string); var TmpLocal: boolean; begin TmpLocal := _AddRefConst(str); // own the lock for the bulk try // because B is not locked UnLockString(Str); // MakeTheRefCountPositive(str) unlock before B B(st); LockString(str); // MakeTheRefCountNegative(str) lock before C C(st); UnLockString(Str); B(st); LockString(str); C(st); UnLockString(Str); B(st); LockString(str); C(st); UnLockString(Str); B(st); LockString(str); C(st); finally _ReleaseConst(str, TmpLocal); // no lose end with locking means no memory leak; hre if the compiler kept track the locking it can call _Release instead end; end; And if the compiler can - and it in fact it does- track of the status of the lock then it is one instruction for both LockStr and UnlockStr, Also the same method can be used with the second code sample when the compiler detect the existence if function like C, but this is more rare code i think. That was fast thinking from me and hope i am still not missing something 🙂 But still the possibility of moving the locking/unlocking from A to C is viable, but this should be the compilers developers work to investigate and test, right ?
  15. Kas Ob.

    The Case of Delphi Const String Parameters

    I am thinking that might be it is easier or possible if the RefCountConst didn't increase RefCount at all, like in A (when calling B) and just negate, this might also save performance, Also in case we are calling C from A, this might be thought deeper there might be a better approach, not sure if it is needed to begin with if C is not calling any assigning function, the same for A, the compiler should be able to check if such called been made, hence the add-try,,finally-dec is needed.
  16. Kas Ob.

    The Case of Delphi Const String Parameters

    No the old and usual ones, because we didn't made the switch here, we are passing var to var, the new ones will be only added when we make the transition for these managed types (string, TBytes...), when they are var and will be passed as constants. Just like you did it in the above code. In fact if such mechanism exist we might be able to utilize it in locking managed types. Also in theory with global consts initialized to 0, with the suggested mechanism, i think we can drop the interlocking instruction, returning/saving very precious +18 cycle per call and the one of most expensive operation with string handling, this is huge, unless i am missing something.
  17. There is many ways to find it but the best and fastest way in this case is to use Handle from sysinternals https://docs.microsoft.com/en-us/sysinternals/downloads/handle it is command line so run it like this then open that 1.txt file and search for your file.
  18. Kas Ob.

    The Case of Delphi Const String Parameters

    Both in fact with the minimum changes. So in code how it should go, as you added the try finally for A,B and C, now we should optimize where parameter is const is an advantage so lets assume only B will have const parameter and currently the compiler will generated code like this procedure C(st: string); begin _AddRef(str); try Writeln(st); finally _Release(str); end; end; procedure B(const st: string); begin C(st); end; procedure A(st: string); begin _AddRef(str); try B(st); finally _Release(str); end; end; we removed _AddRef, _Release and try..finally from B for perfromance, now how to protect against st in B from being changed in C and obey the contract with A, in other words when A issued a call with value of st that should be const it should be returned untouched, we know from earlier posts how the corruption happen (overwritten data), so the idea is changed the mechanism to protect st from the point A called B until return, we are not obliged to implement full LockCounter (we could but why go through all of it) we need to signal ownership only. Here an example omn how to do it, lets add another set of _AddRefConst and _ReleaseConst with little difference from AddRef and Release like this _AddRef if the RefCount is -1 do nothing else increase RefCount _Release if -1 do nothing else decrease RefCount if RefCount is 0 then free (or call destroy/unallocate in short release for good) , (notice UStrClr will return nil after free or not) the suggested behaviour is as the following Global constant strings will have RefCOunt=0 _AddRef increase the RefCount, (this step i think might be simplified as i mentioned in the last assembly) RefCount will stay 0 if it is 0 will be increased by +1 if positive or decreased by -1 if negative _Release decrease the RefCount as above means 0 will stay 0 and if the RefCount is +1 then free for good, now check if the RefCount =-1 and raise an exception, we have violation and/or memory leak in other word unpredicted behaviour. _AddRefConst will do the same as _AddRef except it will negate the RefCount and return boolean true (or anything to distinguish) for ownership if the RefCount was positive _ReleaseConst will do exactly the same as _Release with one exception, it will accept additional parameter for ownership if true then negate the RefCount Now the code above will look like this procedure C(st: string); begin _AddRef(str); try Writeln(st); finally _Release(str); end; end; procedure B(const st: string); begin C(st); end; procedure A(st: string); var TmpLocal: boolean; begin TmpLocal := _AddRefConst(str); try B(st); finally _ReleaseConst(str, TmpLocal); end; end; Notice that 1) the changes from the compiler is done only in A where st is var and will be passed as const, B and C we didn't add any overhead to the existing generated code, so no compiler modification needed there, we handling thing in RTL for B and C 2) in A we need one local var to store ownership, ownership (for lets say strings) will be one and one only, so any consequence functions will not negate what is negative and will return false for ownership, hence we created consistency without implementing full LockCount, this is essential to solve the chaining problem var->const -> var(default) -> const ->..... the first will always have ownership and the data will return untouched. 3) the compiler is already generating temporary local var for its generated code almost everywhere due the small amount of CPU registers, so one more is an overhead of course but still a solution to that memory leak and guarantee its detection. I think that is it, this will guarantee string variable passed as const to be protected, and here i don't mean multithreading, but simple conflict by reusing reference (pointer), so the before mentioned bug ( as i classified it) can be put to rest. Now another approach can be a LockCount by putting it before the others in the string header, we will be breaking strings compatibility between different compilers version as long the RefCount is not reaching 0, this might be a problem and might be not, because from an EXE with an extra counter (replacing the negative algorithm above ) will and can pass a string to dll build with the old scheme and it will work fine as long as the string is referenced from EXE and the bug with const is not in the DLL, so for compatibility this approach is might be little better, though it might need deeper thinking.
  19. Kas Ob.

    The Case of Delphi Const String Parameters

    You are right about interlocked necessity for global const, and here as we discussing mechanism we need to find a way to handle this, and i really like this way of discussing of viability instead of no and wrong and "none sense stuff" !!, thank you So what if the global const are initialized with 0, plain and simple, 1) We are safe as 0 will not be reached form 1 because it will automatically call free, and from -1 it will be safe as it should not happen or an exception is due. 2) This will introduce little overhead in calculating the addition (1 or -1) and in this case we still can mange to do it branchless like this xor eax, eax test edi, edi setg al lea eax, [rax + rax - 1] // lea eax, [eax + eax - 1] cmove eax, edi Thank to godbolt and clang for this great and god given power in solving such algorithms, saving huge time tryin to figure a fast assembly ! The above will -1 for negative value and +1 for positive value and 0 for 0, means we can also go ahead and remove the LOCK (interlocked operation) for the RefCount and as explained in (1), 0 as refcount can only be overwritten by 0, and we still branchless for this case. Did i miss something ? and what do you think ?
  20. Kas Ob.

    The Case of Delphi Const String Parameters

    I am sorry to disagree here, in my opinion were added as futile try to make strings thread safe, and all what achieved is the fake sense of safety, i am sticking to my description of them as useless and wasting at least +18 cycle per call. You can call it tricky, but lets simplify this algorithm and mechanism to its most basic then build on that Since a call like UStrAsg is been called on any strings (const or not), means we are reaching the comparison to -1, then if it is -1 then branch to skip the addition, so what gain of of not adding 1 to it, and to explain this little deeper, we can have faster const string by initialize them to 1 or even 10 and skip compare and jump, and here i am talking about how CPU's works and where they does waste cycles, and yes a simple add (without LOCK) is way faster and performance friendly than compare and jump. Also if it is initialized to 1 then it is impossible to reach 0 and then trigger the free on global const string, because this will happen with memory corruption or broken try..finally, considering the const strings are already allocated and initialized in read-and-write memory. if it is -1 or 3 or whatever the overwrite or free is happening in one case, and this is when RefCount goes from 1 to 0, simple and clear. So after we simplified the mechanism so much, don't you think it is possible to do it better ? and faster many folds ?
  21. Kas Ob.

    The Case of Delphi Const String Parameters

    My LockCount theory wasn't for threading at all, it was for marking the content as mute, simply marking the associated data as immutable (aka temporary const ), to correct and prevent the leak mentioned in earlier posts. Please have a look on how they are implemented for strings and you will see, they are not for thread safety as there is a check then lock (inc/dec), this is wrong and ineffective without a loop for thread safety, in other words useless.
  22. Kas Ob.

    The Case of Delphi Const String Parameters

    I did not ! Why they are interlocked to begin with ?, are strings thread safe to waste 22-25 cycle on one instruction (18 on modern processors) ? No they are not thread safe, so what the point for having interlocked operation else than waste time. Even though, with interlocked instruction we still adding a value, a value that is either -1 or +1 after checking we are not crossing from -1 to 0, i explained that already many times. The point is i am not introducing huge change to the functionality in the RTL, small changes that only, instead of adding 1 always, it will keep a flag bit as protection, while the required to be added code will be minimum and only done by the compiler when a var declared value will be passed to as const parameter, nothing else, this will ensure protection of that associated data or value or whatever you want to call it, also the added code is merely few instruction not everywhere as Dalija wrongly assumes.
  23. Kas Ob.

    The Case of Delphi Const String Parameters

    @Dalija Prasnikar You still didn't get it, and your argument of full of fallacies, based on miss understanding the idea to begin with. I am not introducing any new reference counting in new places (were currently is not already there), my suggestion is to lose a fraction of the that gain to secure the code functionality and correctness of the code. No, it is not, it will prefect it and just complete the design. You can NOT be more wrong about this assumption, the problem is not the compare itself but what comes after, the branching, your assumption will be true on the CPU's last made in 1997. Sure, but this is for something we write code for, like object so we must check assigned() first then write the code, we can't and shouldn't be managing simple types the solemnly is the compiler responsibility, this should be de facto for any language, also the checks to protect against broken code should be provided by the compiler as built-in feature. I broke my word to not comment anymore, but these above are personal advice for you, read what i wrote and if the idea is not clear then we discuss mechanism and clear things, not simply throw any idea away, because it is discussing taboo based on our biased and wrong knowledge, and of course our refusal to to agree it can be better. Really sorry for wasting your time.
  24. Kas Ob.

    The Case of Delphi Const String Parameters

    This took too much discussion and i am giving up, to summarize what we have as facts 1) Literal string could be initialized to +5 ( or any positive value) by the compiler and this will not break anything, RTL doesn't need the part for checking for -1, we can agree on this , right ? if it is 1 before executing any code then it is impossible to reach 0 and trigger exception by returning it to MM, and considering the compiler and RTL are executing the code of accessing the RefCount in constant or not, then this mean inefficiency on both RTL and compiler too, so at least this can be changed for performance. 2) If we introduced another field for (lets say) strings, something like LockCount in the strings header, we could have reached the ultimate protection for strings, and here we have this a) we didn't lose much bytes as all MM with Delphi is multiple of 8 at least, and the current header is 12 size. b) by making the header size 16 byte we managed to align the chars sequence in string hence, then we achieved a good optimization with the possibility to aligned SIMD out of the box, assuming the MM is 16 aligned. c) if that is not bad design, because it is not, also will guarantee the consistency of the compiler and the language, then are we allowed to think of optimize this to lose the new field and lose the alignment and achieve better performance by replacing it with a single bit on already existing field? 3) Introducing or just discussing any better code is wrong and off the table, because the compiler and RTL is not doing it, great idea and principle to evolve and enhance, here i would suggest 30 minute video on how these lost and helpless people calling themselves developers at FaceBook playing around with strings, introducing stuff and changing stuff, shame on them, they don't have a lick of understanding on how strings should work. https://www.youtube.com/watch?v=kPR8h4-qZdk So in short, it is not a bug, and will not be fixed, fine and good luck all. ps: i wished if someone just thought about this through and asked how my suggestion flawed in case of chaining functions, and the answer if very simple and short (also easy to find), i skipped explaining to see if someone would ask !, no one care !, as a hint to wondering minds as i am leaving this, at least 10% of code in Delphi application is handling and shuffling values on stack 😉
  25. Use DDetours https://github.com/MahdiSafsafi/DDetours Look at the demo which is hooking MessageBox API, study it then hook the following API's and you will have the culprit CreateFile CreateFileEx The thing is you need CloseHandle to pair up each create with close, but CloseHandle might be tricky as it is used with many handle types, so by only tracking CreateFile, you can find the last one succeeded in opening/creating the file and didn't close its handle yet.
×