Jump to content

Stefan Glienke

Members
  • Content Count

    1366
  • Joined

  • Last visited

  • Days Won

    130

Posts posted by Stefan Glienke


  1. 2 hours ago, JonRobertson said:

    But I believe a lot of what "operates the world" is line of business apps, whether they be for commerce, government, health care, or hundreds of other industries.

    And unless they are implemented as thick 2-tier clients directly accessing some shared database they probably talk to some backend code where the interop with other systems is implemented.

     

    But let me make a slight correction about the particular Delphi situation: I know some people successfully build mobile apps but the majority are Windows (and some Mac OS) desktop applications

    I am also leaving out all the web stuff because Delphi does not play a significant role in that area (yes, I know about the various frameworks for doing web stuff with Delphi), and depending on what technology is used most of it is backend code.

     

    There has to be some reason why some recently very famous programming languages don't have some easy-to-use UI frameworks - either because they run almost everywhere including your toaster which makes it hard to provide some all-in-one solution (how often have UI frameworks tried that already?) or because it's code that does not require some rich UI.


  2. Why are Delphi developers so obsessed with doing GUI - I assume most software that operates the world is non-GUI stuff. And I also believe that this is typically the software that should be rock solid. The code that crashed Ariane 5 or caused security vulnerabilities in the recent past was hardly some GUI application.

     

    And Delphi code is only comparably fast with other languages that are top-tier in that category when you write it in a non-idiomatic way (i.e. pointers) - read some mormot Code if you don't believe me.

     

    Just one example:

    In other languages it does not matter if you are using some indexed-based loop (if that is even allowed) or some for-in/for-each loop - the compiler there knows its stuff and turns it into the fastest code possible while not sacrificing any safety.

    Sometimes the performance is even better when you are using built-in functions because internally the runtime and/or compiler devs did some incredible work optimizing stuff. Here is just one of the many examples of .NET 7.

     

    In Delphi, you almost always pay a cost for every abstraction although the compiler could make it zero-cost - one of the major tasks of modern compilers: enable the developer to write idiomatic and descriptive/declarative code without sacrificing performance.

    • Like 4

  3. TValue has no implicit casting rule for integer -> enum - thus it raises the invalid cast exception because you are passing an Integer to SetValue which gets implicit put into a TValue which gets transported further. It later does a type/cast check against the actual type of the property and fails.

    See TValue.Cast or TValue.TryCast and the Conversions matrix for more details.

     

    I mentioned this many times: TValue is not Variant - it only supports implicit type casts that also the language supports and Integer to Enum type and vice versa is not directly assignable.

     

    procedure SetEnumProp(Instance: TObject; const PropName: string; const Value: string);
    var
      c: TRttiContext;
      t: TRttiType;
      p: TRttiProperty;
      typInfo: PTypeInfo;
      enumValue: Integer;
      v: TValue;
    begin
      t := c.GetType(Instance.ClassInfo);
      p := t.GetProperty(PropName);
      typInfo := p.PropertyType.Handle;
      enumValue := GetEnumValue(typInfo, Value);
      v := TValue.FromOrdinal(typInfo, enumValue);
      p.SetValue(Instance, v);
    end;
    • Thanks 2

  4. 1 hour ago, Ian Barker said:

    A constant you can assign at run time? Is that a one-off thing - assign once and then it's immutable? If not, then it's a variable, not a constant or have I misunderstood?

    Imagine you could write this code in Delphi:

     

    var
      i: Integer = 42;
    begin
      const j = i;
      Writeln(j);
    end.

    Oh, wait, you can.

     

    Too bad that the syntax is not really clean as it mimics the const declaration using the equal sign while it really is what other languages call immutable variable and thus should have used the assign operator but people with even less expertise in programming language design than me disagreed.

    • Like 2

  5. Differences in microbenchmarks can have all kinds of reasons (*) - when talking about the performance of single instructions you never estimate them from some possibly flawed microbenchmark but consult the instruction timings table (search for CVT(T)SS2SI) - the fact that truncate and non-truncate are always listed together makes it obvious that they perform exactly the same.

     

    (*) code alignment or address of the measured functions being one of the many reasons that can easily make some small or significant differences in the results

     

    All these tiny gotchas are the reason why many people don't like microbenchmarks. They are one tool for measuring but don't tell the ultimate truth - especially when it comes down to only a few instructions.

     

    That being said here are the results from an i5-13600K:

     

    x86
    -----------------------------------------------------------------------------
    Benchmark                                   Time             CPU   Iterations
    -----------------------------------------------------------------------------
    FastTrunc/Trunc:10910016                 5585 ms         3703 ms            1
    FastTrunc/FastTrunc_SSE2:10910032        2081 ms         1234 ms            1
    FastTrunc/FastTrunc_SSE41:10910120       2158 ms         1047 ms            1
    FastTrunc/SlowTrunc_SSE2:10910048        4193 ms         2641 ms            1
    
    
    x64
    
    -----------------------------------------------------------------------------
    Benchmark                                   Time             CPU   Iterations
    -----------------------------------------------------------------------------
    FastTrunc/Trunc:12750304                 5793 ms         3750 ms            1
    FastTrunc/FastTrunc_SSE2:12750336        4775 ms         3656 ms            1
    FastTrunc/FastTrunc_SSE41:12750432       6364 ms         4703 ms            1
    FastTrunc/SlowTrunc_SSE2:12750352        4808 ms         2703 ms            1
    
    

    Take these results with a grain of salt and keep in mind two things:

    - Spring.Benchmark still has some issues when running on Intels hybrid CPUs (12th and 13th gen) - I can trick a bit with setting Thread Affinity masks to run only on P-Cores but sometimes the times are a bit off

    - on x64 we might experience the behavior of implicitly converting Single to Double and back - I did not inspect the assembly code.


  6. 3 hours ago, Remy Lebeau said:

    You can't manually implement the interface of an anonymous method.

    Yes you can, the first post in this thread contains the code doing exactly that.

    3 hours ago, Remy Lebeau said:

    They could, and that shouldn't change anything at all, because user code shouldn't ever be calling Invoke() directly to begin with.

    In fact, until some version a few years ago it was possible to call Invoke.

    Still, if the method reference type has {$M+} enabled you can get RTTI for its Invoke method and dynamically invoke it. And there are more than one (Spring) libraries that do that.

     

    Also please let's get the terminology right - this is something that hugely annoys the heck out of me - because when talking about this subject everything is being called anonymous method but is incorrect.

    TProc = refererence to procedure;

    This is a method reference type - yes, even the official documentation is a mishmash. There is nothing anonymous about this type - it has a name:  TProc.

    procedure Foo;
    begin
    end;
    
    var
      p: TProc;
    begin
      p := Foo;
    end.

    Again there is no anonymous method in this code - the variable p of the method reference type TProc is being assigned. The variable has a name and thus also is not anonymous.

    var
      p: TProc;
    begin
      p := procedure begin end;
    end.

    Now we have an anonymous method - the code block assigned to p has no name. This is an anonymous method that is assigned to the method reference variable p.

     

    Also see:

    https://en.wikipedia.org/wiki/Closure_(computer_programming)#Anonymous_functions

    https://en.wikipedia.org/wiki/Anonymous_function

    • Like 3

  7. As someone using this "feature", I can tell you that the moment they break it I will pester them with issue reports until they revert this "fix".

     

    There has been another hidden "feature" in the past: adding operator overloads to records via helper when they are named in a particular way - since a few Delphi versions you can do that natively by declaring operator overloads on record helpers without this "hack" - but the hack still works.

     

    Since there is no official language specification and it's basically "if it compiles, it's valid code" I assume this to be added to the documentation rather than to be changed. Embarcadero has not changed things that are worse for backward compatibility reasons in the past so I assume they won't touch this either.

     

    The more important thing they should address with anonymous methods is this: not allocating any heap memory when no capturing is happening. The code would look similar to that for the comparers for intrinsic types in System.Generics.Defaults.pas

    • Like 1

  8. SHA1 is dead. I suggest using SHA2 or SHA3 even if it is "just" for a file checksum. If you want performance-optimized implementations I would suggest using mormot2.

     

    uses
      mormot.core.buffers,
      mormot.crypt.secure;
    
    ...
    
      HashFile(myFileName, THashAlgo.hfSHA256);  

    Fun fact: mormot2 SHA256 is faster than RTL SHA1.

    • Like 3
    • Thanks 1

  9. It is because the compiler does not keep typeinfo of types that are only used as generic parameters in generic types in the public typeinfo list (which is what FindType with a qualified name iterates).

    If you had included the code for that Unmarshal I could probably give more advice on how to implement it best given that you seem to have support for spring collections in there

    • Thanks 1

  10. 1 hour ago, JonRobertson said:

    The memory manager would have to "know" when the memory was no longer needed, otherwise it would release allocated memory too soon.

    This would be called Tracing garbage collection.

     

    But as Dalija already explained the crux is not which way of automatic memory reclamation is being used but the fact that it would be a mix of different ones that don't play together nicely.

    • Like 1
×