David Heffernan 2345 Posted June 10, 2020 4 hours ago, Mahdi Safsafi said: The code showed clearly that they do on x64!!! Only for a totally unrepresentative example, and only because of implementation defects in that compiler. Not because there is anything conceptual. Feel free to choose between true and typed constants because of a few milliseconds difference in compilation time. If that means something to you, great. Knock yourself out. For me I will stick to what results in readable and maintainable code, and code that runs most efficiently. Share this post Link to post
Guest Posted June 10, 2020 48 minutes ago, David Heffernan said: Only for a totally unrepresentative example, and only because of implementation defects in that compiler. Not because there is anything conceptual. You are wrong here. Does this code show a defected compiler? procedure Test; const untypedC = 1234567; typedC: Integer = 1234567; var I, J: Integer; K, L: Int64; begin I := untypedC; J := typedC; K:=untypedC; L:=typedC; if I = J then Exit; if K = L then Exit; end; Assembly is like this No, the compiler here is generating right code, can be better yes, but that is not the point, the example you need to see is that for typed constants, there is extra assembly instruction(s), generating 2 asm instruction can't and will not have the same speed of generating one, hence untyped constant is faster to compile than typed constant. Can you accept this as example ? Share this post Link to post
Guest Posted June 10, 2020 It is more clear with this, after moving both const and var to global from local resolving relative address's will be take more time from the compiler, noticeable or not. Share this post Link to post
David Heffernan 2345 Posted June 10, 2020 (edited) 38 minutes ago, Kas Ob. said: Can you accept this as example ? Now you are talking about the generated code. Which is a different issue. Hitherto there has been a long, and in my view bogus, discussion about compilation speed. The OP said that compilation speed was a reason to prefer avoiding typed constants. The point that I made which seems to have generated such noise is merely that speed of compilation is no reason to prefer true constants over typed constants. EDIT: I didn't read closely enough. You are claiming that the compile time must be longer because more instructions are emitted? Wow, that's weird. Known to be false also. Consider optimisation. Often this results in fewer instructions emitted, post optimisation. But optimisation is an additional step that can increase compile time There are good reasons to prefer true constants over typed constants. Compilation speed isn't one of them. Efficiency of generated code is one. Frankly I'm ambivalent about what you both think. If you want to change the way you code to save a couple of milliseconds of each compilation, then do it. It doesn't bother me. Edited June 10, 2020 by David Heffernan 1 Share this post Link to post
Lars Fosdal 1791 Posted June 10, 2020 My most common use of typed constants is records containing translation strings. These are used for in-place translation of errors and prompts in JsonRPC responses, instead of the typical translated resource solution you would use in a desktop application. This is done to reduce the cost of lookup, since the same server will be required to respond to requests in multiple languages. It works so well that we've started using the same system for configuring text for grids, etc. - just to simplify the process of adding new content without requiring more translation work in a separate tool. For me, the benefits are that the translations are kept in one place - so it is next to impossible to mistranslate a text, or forget about adding a translation in a specific place when adding a new language and since the texts are in the source code, it is trivial to add new translations. I have hundreds of these typed constants and if they do add compile time, it must be next to nothing, because I can't say that I notice any slowdown in the compilation. My main gripe is that I cannot pass these as parameters to an attribute. Here is a simplified albeit rather contrived example but it illustrates my point. program TypedConsts; {$APPTYPE CONSOLE} {$R *.res} uses System.Classes, System.SysUtils, Vcl.Graphics, Generics.Collections; type TxStyle = record font: string; size: integer; attr: TFontStyles; end; type TextStyle = record const // typed consts within a record to create a pseudo namespace Title: TxStyle = ( font: 'Arial'; size: 12; attr: [fsBold]); Chapter: TxStyle = ( font: 'Arial'; size: 10; attr: [fsBold]); Section: TxStyle = ( font: 'Georgia'; size: 10; attr: [fsBold]); Normal: TxStyle = ( font: 'Arial'; size: 9; attr: [fsBold]); Code: TxStyle = ( font: 'Courier New'; size: 9; attr: []); Hint: TxStyle = ( font: 'Garamond'; size: 8; attr: [fsItalic]); OhNoes: TxStyle = ( font: 'Comic Sans MS'; size: 8; attr: [fsItalic]); end; type FontAttribute = class(TCustomAttribute) Style: TxStyle; constructor Create(const aStyle: TxStyle); overload; constructor Create(const aFont: string; const aSize: Integer; aAttr: TFontStyles); overload; end; type TText = class private FText: String; FStyle: TxStyle; published property Text: String read FText write FText; property Style: TxStyle read FStyle write FStyle; end; TParagraphs = TObjectList<TText>; //{$define UseRec} // Define to use the record format, undefine to use the individual fields // Regardless of defined or not, this does not compile. TDoc = class // would have a method to crawl the type RTTI and initialize the style attributes. private FTitle: TText; FBody: TParagraphs; public {$ifdef UseRec} [Font(TextStyle.Title)] {$else} [Font(TextStyle.Title.font, TextStyle.Title.Size, TextStyle.Title.attr)] {$endif} property Title: TText read FTitle write FTitle; {$ifdef UseRec} [Font(TextStyle.Normal)] {$else} [Font(TextStyle.Normal.font, TextStyle.Normal.Size, TextStyle.Normal.attr)] {$endif} property Body: TParagraphs read FBody write FBody; end; { FontAttribute } constructor FontAttribute.Create(const aStyle: TxStyle); begin Style := aStyle; end; constructor FontAttribute.Create(const aFont: string; const aSize: Integer; aAttr: TFontStyles); begin Style.font := aFont; Style.size := aSize; Style.attr := aAttr; end; begin try { TODO -oUser -cConsole Main : Insert code here } except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. 1 Share this post Link to post
Guest Posted June 10, 2020 The OP wrote a very good and nice post explaining how compiler works with Constants, and shows deep knowledge and understanding of the compiler and compiling in general. It is you who wrote this. On 6/8/2020 at 11:40 PM, David Heffernan said: On 6/8/2020 at 11:12 PM, Mahdi Safsafi said: You don't need to measure the elapsed time You do. If you can't provide a real world example where compilation time is significantly impacted, then I call BS. David, calling BS for what is not wrong is wrong, and here OP is right (not wrong but right). 31 minutes ago, David Heffernan said: You are claiming that the compile time must be longer because more instructions are emitted? Wow, that's weird. Known to be false also. Consider optimisation. Often this results in fewer instructions emitted, post optimisation. But optimisation is an additional step that can increase compile time Right, no one arguing here about optimization (no one mention it doesn't mean we are oblivious about it), for exactly this reason i provided the second one, which faster in compiling than the first one, as all were global instead of local, and specially pointed to the "rel" in 64bit, do you see any optimization needed here? no. You were asking for one example but here you took words out of the context of one example that been requested, and made a claim that we recommend to change your coding style. David, no one suspect that you have more knowledge than many ( may be all) us here in many fields, though you can't be knowing everything all the time in every detail, so please prove you point or drop it, don't steer the subject left and right. And saying "i was wrong" will not make you lesser person or developer, on contrary you will gain more respect instead of losing some by offending person in person without proof, just using you reputation not your knowledge. @Mahdi Safsafi Thank you for this thread, and i really hope you will continue posting here such good articles, if you have blog post then please share with us, if not please consider posting threads here, many or some will find it helpful and will appreciate it. Note: i would love to read some about optimization window size of the compiler conditional jumping in modern CPU's ( how to trigger out-of-order-execution), this will not only help me but might help Delphi guys too, as Delphi compiler have 0 consideration for this part, i researched this subject many times over many years, but genuinely believe you can provide better research info or at least resource's to read. Share this post Link to post
David Heffernan 2345 Posted June 10, 2020 1 hour ago, Kas Ob. said: David, no one suspect that you have more knowledge than many ( may be all) us here in many fields, though you can't be knowing everything all the time in every detail, so please prove you point or drop it, don't steer the subject left and right. And saying "i was wrong" will not make you lesser person or developer, on contrary you will gain more respect instead of losing some by offending person in person without proof, just using you reputation not your knowledge. A few milliseconds during a typical compilation is insignificant. That's my opinion. Others may have a different opinion. They are welcome to it. 1 Share this post Link to post
David Heffernan 2345 Posted June 10, 2020 The biggest issue with constants in Delphi are that typed constants can't be used in certain settings which require true constants to be used. The ones that come to mind are: 1. When declaring typed constants. 2. When declaring attributes. The inability to used typed constants in these settings is clear weakness in the language. There are probably more issues, but these are the ones that bite me. I'm tired of hearing justification for these limitations based on the current implementation. All this talk about single pass, interface vs implementation, writeable typed constants, etc. If the implementation limits you, change it. 2 Share this post Link to post
Guest Posted June 10, 2020 25 minutes ago, David Heffernan said: The biggest issue with constants in Delphi are that typed constants can't be used in certain settings which require true constants to be used. The ones that come to mind are: 1. When declaring typed constants. 2. When declaring attributes. True, and adding the the compiler inconsistency between typed and untyped consistent ( the claim i was trying to prove in other thread), here an example procedure Test2; const untypedC = $7FFFFFFFFFFFFFFF ; // max positive value for int64 (signed integer) typedC : Int64 = $7FFFFFFFFFFFFFFF ; // max positive value for int64 (signed integer) var I1, I2, I3, I4, I5, I6, I7, I8, I9: Integer; // max positive for this type should be $7FFFFFFF I64: Int64; begin I1 := untypedC; // compiler will warn here I2 := Int64(untypedC); // compiler will warn here Integer(I3) := untypedC; // compiler will warn here Integer(I4) := Int64(untypedC); // compiler will warn here I5 := typedC; // compiler is not warning here I6 := Int64(typedC); // compiler is not warning here Integer(I7) := typedC; // compiler is not warning here Integer(I8) := Int64(typedC); // compiler is not warning here I64 := $1234; I9 := I64; //Integer(I64) := I9; // error : Left side cannot be assigned to //PInteger(@I64)^ := ShortInt(I9); // workaround if I1 = untypedC then Exit; if I2 = untypedC then Exit; if I3 = untypedC then Exit; if I4 = untypedC then Exit; if I5 = typedC then Exit; if I6 = typedC then Exit; if I7 = typedC then Exit; if I8 = typedC then Exit; if I9 = $1234 then Exit; // on 64bit with optimization. if is using I64 as Integer(I64) and (I9 := I64) is skipped above end; The in ability to compile "Integer(I64) := I9;" is very annoying, yet i am living without it. But how warning are generated ( or lack of them), they will bite you when you add RangeCheck only with overflow value, this might skip you in developing and even in testing, and manifest in production. I think this should be in the other thread to continue my fight with Mahdi 😄 , he managed to evade providing direct answer if the Delphi compiler is right or wrong. about attributes, i hate them, not saying i don't use them, just simple pure hate, due the lack of ability to see them in code when browsing, my brain handle them as comments in the first pass, but on the second pass i get it. Now to this 43 minutes ago, David Heffernan said: All this talk about single pass I think that is unfair to ask this, single pass or two, the problem with this term that it is refer (or can be used with different things), it is unclear, so may be you fully understand the difference between a pass inside compiler within the of window generating code for one function, and the pass afterward when building it with different functions, see, you yourself provided an answer to that exactly (different thread) when you hinted that record forward declaration can be done as class forward declaration, i know what my words means and fully understand it, here in this case we are talking about the declaration and interface, and this can be done in one pass ( the second one ) in the first pass ( the interface building), so you go and list with what you have then expand later, we didn't leave the sector/section or the interface declaration pass itself, right ? Share this post Link to post
Guest Posted June 10, 2020 (edited) About the pass's term and definition , i would love to read Mahdi explanation here, of course correcting me if i miss explained it or i don't fully understand it, so Mahdi if you got time please shed some light on that, clearly he got better English, as i got the bad English and Memory. EDIT: Ops, i forgot the main point if that example and pass, proving i have short living memory, in that example i point that the compiler had skipped this line "I9 := I64;", how could it did that if there wasn't second pass within that function ?! On other hand a compiler with more than one pass, here i am refereeing to real modern compiler, would have that function skipped in all !, it has 0 input and 0 output, means no effect on any logic of the code and can be ditched in full. Hope this clear things. Edited June 10, 2020 by Guest Share this post Link to post
Mahdi Safsafi 225 Posted June 10, 2020 @Kas Ob. Quote David, calling BS for what is not wrong is wrong, and here OP is right (not wrong but right). That made me angry ... He showed a disrespect for me. He didn't appreciate the detailed explanation I gave neither the time I waste nor the experience I have. First, I said that the reason behind why Delphi's compiler not allowing using complex-type constants (e.g: array, record) as true constants was compile time speed (and I clearly explained why ? as this involves processing the data for each unit that uses the const ) ... He refused to believe and said that the reason because the types are complex ! ... I gave him a day to day example from the D programming language just to prove that the complexity is not a factor ! And yet he answered : That's not a Delphi Style. Second, I said that any developer should avoid using typed-ordinal-type-constants(Byte, Integer, ...) as this will impact optimization and adds an extra overhead at the compilation-time... Yet he strongly believed that's not true ! ... Again, I gave him a good explanation (how linker work) ... He called my explanation BS and asked for tests ! I made a test and showed clearly that at least the x64 compiler was affected ... Yet he said that the test isn't a real-world test and blamed the poor implementation for x64 compiler ... And again I found my self clarifying his wrong beliefs (explaining why it's hard to an x64-toolchain to outperform an x86-toolchain). Indeed he is experienced in Delphi development, but still has a lot to learn -thinking out of the box- (using other toolchain, knowing how a linker work wan't hurt too, ...) ... this certainly will change his mind about many things and would make him open for argumenting (not because Delphi is doing it in an X way means that Y is wrong). This applies to everyone ... including me (learning is a good way to open your mind)! 3 Share this post Link to post
Guest Posted June 10, 2020 47 minutes ago, Mahdi Safsafi said: but still has a lot to learn -thinking out of the box- (using other toolchain, knowing how a linker work wan't hurt too, ...) And i might here add as suggestion to him and few others: to refrain from using the straw man fallacy https://en.wikipedia.org/wiki/Straw_man , many developer in this forum are using this broken logic repeatedly, we are not in enemy or in a contest for likes, right ? Please show respect, all in all there is no reason go after a person himself (by offending or disrespect) because you think he is wrong, even if he is wrong. Share this post Link to post
David Heffernan 2345 Posted June 10, 2020 57 minutes ago, Mahdi Safsafi said: I said that any developer should avoid using typed-ordinal-type-constants(Byte, Integer, ...) as this will impact optimization and adds an extra overhead at the compilation-time Compilation time is not a valid reason to make that choice. I stand by that. Your own measurements backed up my point of view. 58 minutes ago, Mahdi Safsafi said: Yet he said that the test isn't a real-world test It wasn't. 58 minutes ago, Mahdi Safsafi said: explaining why it's hard to an x64-toolchain to outperform an x86-toolchain I understand that, but there's no good reason why they shouldn't have the same relative characteristics. So whilst the x64 compiler may well be slower than x86, the x64 compiler should not blow up when presented with huge numbers of typed constants, in a way that the x86 compiler does not. 59 minutes ago, Mahdi Safsafi said: I said that the reason behind why Delphi's compiler not allowing using complex-type constants (e.g: array, record) as true constants was compile time speed A more plausible explanation to me is simply the history of writeable typed constants. At this point we all know what each other thinks, and there's little point repeating ourselves any more. Share this post Link to post
David Heffernan 2345 Posted June 10, 2020 4 minutes ago, Kas Ob. said: And i might here add as suggestion to him and few others: to refrain from using the straw man fallacy https://en.wikipedia.org/wiki/Straw_man , many developer in this forum are using this broken logic repeatedly, we are not in enemy or in a contest for likes, right ? Please show respect, all in all there is no reason go after a person himself (by offending or disrespect) because you think he is wrong, even if he is wrong. I don't think that anybody here has used a straw man. And only two people have made any statements directed at an individual, rather than concentrating on the arguments. Share this post Link to post
David Heffernan 2345 Posted June 10, 2020 1 hour ago, Mahdi Safsafi said: He showed a disrespect for me I didn't mean to show you any disrespect. I'm sorry that it came across that way. All I meant was that I thought the argument that compilation speed was important was bogus. I really did not mean to upset you. I try hard never to make anything I write personal. To me it's always about the technical details. 4 Share this post Link to post
Lars Fosdal 1791 Posted June 10, 2020 Overly simple Compilation Speed Test The attached project has a define. {$define AsTyped} When defined, the Texts record contains 500 typed constants. When not defined, the Texts record contains 500 constant strings. On my rather busy laptop, the compile time varies between 0.8 to 1.3s for both with or without the define. TypedConsts2.dpr Share this post Link to post
Mahdi Safsafi 225 Posted June 10, 2020 Quote A more plausible explanation to me is simply the history of writeable typed constants. Adopting the historical reason is in somehow wrong ... because the Pascal-ISO didn't introduce typed-constants neither did the Extended-Pascal-ISO. FYI, I'm studying both ISO for a personnel usage. // A definition of constant in Pascal-ISO: constant-definition = identifier '=' constant . constant = [ sign ] ( unsigned-number | constant-identifier ) | character-string . constant-identifier = identifier . // A definition of constant in Extended-Pascal-ISO: constant-definition = identifier '=' constant-expression . constant-identifier = identifier . constant-name = [ imported-interface-identifier '.' ] constant-identifier . // Moreover : extended pascal allowed this : const BlankCard = PunchedCard[1..80: ' ']; blank = ' '; Origin = subpolar[r,theta:0.0]; column1 = BlankCard[1]; MaxMatrix = 39; pi = 4 * arctan(1); mister = 'Mr.'; As you can see the Extended-Pascal allowed a more relaxed form of constants ! So saying that the reason is related to historical reason can't be adopted. const MyData: array[ 0 .. 1000] of integer = (...); const element = MyData[1] + 1; // if the compiler allowed this ... it will come at a cost of compilation-speed (I already explained why). Quote Compilation time is not a valid reason to make that choice. I stand by that. I didn't say it's a reason ! I said typed-ordinal-constants are volatile and not well optimized moreover they add extra overhead on compilation. They should not never be used. The example you give(Fortran) could be an exception. Quote Your own measurements backed up my point of view. It would if x64 showed a close result to x64. Quote So whilst the x64 compiler may well be slower than x86, the x64 compiler should not blow up when presented with huge numbers of typed constants, in a way that the x86 compiler does not. I believe(I'm not sure) that's related to the OMF. A good way to find that is to check Delphi LLVM based compilers. If all compilers gave a close result to the dcc64 then it would enforce that ! Unfortunately I don't have any Delphi LLVM compiler on my machine. I'd appreciate from you or anyone else to do the check. Share this post Link to post
Lars Fosdal 1791 Posted June 10, 2020 58 minutes ago, Lars Fosdal said: Overly simple Compilation Speed Test The attached project has a define. {$define AsTyped} When defined, the Texts record contains 500 typed constants. When not defined, the Texts record contains 500 constant strings. On my rather busy laptop, the compile time varies between 0.8 to 1.3s for both with or without the define. TypedConsts2.dpr That was for 32-bit 64-bit compiler appears to have a tad longer compile time for typed constants. 1.2s vs 0.9s for string consts, but the margins are so small and the variation so large, that it is hard to say. Share this post Link to post
Guest Posted June 10, 2020 18 minutes ago, Lars Fosdal said: 64-bit compiler appears to have a tad longer compile time for typed constants. 1.2s vs 0.9s for string consts, but the margins are so small and the variation so large, that it is hard to say. Same here, Using XE8 FMX: the time is same for 32bit ( either 0.9 or 1), but on 64bit have 1s for untyped and (1.4-1.5) for typed. @Lars Fosdal i think your test might work and show better and cleaner result, but need small adjustment. 1) remove the strings from those records to amplify the difference, as handling the strings load itself, will keep the compiler busy for same amount of time. 2) 500 is too little , if you looked at windows api files, the constants easily be few folds more than 500, so increase them to few thousands ( 5k-10k might show visible difference) Share this post Link to post
Lars Fosdal 1791 Posted June 10, 2020 You got the source. Be my guest. 1 Share this post Link to post
Guest Posted June 10, 2020 I thought you used some script to generate it. Share this post Link to post
Mahdi Safsafi 225 Posted June 10, 2020 @Lars Fosdal Thanks for your example ... nice addon ! but I believe there is a misunderstood here. There is a difference between ordinal-type-constant(Char, Byte, Int16, Integer, UInt64, ...) and structured-type-constant(record, array, string, PChar, ...). // these two are different definition : const A: Integer = 2; // Involves linker cooperation. const B = 2; // Does not invlove linker cooperation ... the compiler is able to handle it itself. // ----------------------------------------------------------------------------------------------------------- const Str : PChar = 'this is foo'; // Involves linker cooperation. const Str2 = 'this is foo'; // Involves linker cooperation too. But why it involves linker cooperation ? that's because structured-data must be allocated at the heap ! When compiling, the compiler have no information at witch address Str, Str2 would terminate to ! It will just emit an empty address and add some information about all empty case he left ... later when compiler finished, it gives the linker the necessary information and ask him to fix all the blank address. Now why the linker is able to know the address and compiler does not ? that's because the linker can see all modules (compiler compiles only one module at a time). I believe now you understand why your example isn't a real fair test for demonstrating typed-constants. Share this post Link to post
Lars Fosdal 1791 Posted June 10, 2020 Cut'n paste and RX replace. That said: It is unlikely that I'd have more than a few thousand typed record constants. I.e. The increase in compiler time is for all practical purposes insignificant. 1 Share this post Link to post
Lars Fosdal 1791 Posted June 10, 2020 1 minute ago, Mahdi Safsafi said: I believe now you understand why your example isn't a real fair test for demonstrating typed-constants. Dude, why do you think I wrote 1 hour ago, Lars Fosdal said: Overly simple Compilation Speed Test And again - I really don't care about the speed penalty. I just want constants that are actual constants also when typed and that cannot be changed at runtime. Share this post Link to post
Mahdi Safsafi 225 Posted June 10, 2020 2 hours ago, David Heffernan said: I didn't mean to show you any disrespect. I'm sorry that it came across that way. All I meant was that I thought the argument that compilation speed was important was bogus. I really did not mean to upset you. I try hard never to make anything I write personal. To me it's always about the technical details. Don't worry ... it's not a big deal 1 Share this post Link to post