Jump to content

pyscripter

Members
  • Content Count

    983
  • Joined

  • Last visited

  • Days Won

    62

Posts posted by pyscripter


  1. I was trying to improve the performance of SynEdit in handling files with super long lines (e.g. 50K characters).  In doing so I came across an unexpected optimization that had a spectacular impact on performance.

     

    See this innocent looking function probably written two decades ago (fCasedLine is a PChar):

    function TSynCustomHighlighter.GetToken: string;
    var
      Len: Integer;
    begin
      Len := Run - fTokenPos;
      SetLength(Result, Len);
      if Len > 0 then
        StrLCopy(@Result[1], fCasedLine + fTokenPos, Len);
    end;

    By measuring I found that this function (called zillions of times) was a bottleneck and replaced it with:

    function TSynCustomHighlighter.GetToken: string;
    var
      Len: Integer;
    begin
      Len := Run - fTokenPos;
      SetString(Result, fCasedLine + fTokenPos, Len);
    end;

    The impact was massive.

    Both SetString and  StrLCopy in x64 are implemented in pascal (not assembly) using Move.   So initially I was puzzled.

     

    However here is the reason.  Look at StrLCopy:

    function StrLCopy(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar;
    var
      Len: Cardinal;
    begin
      Result := Dest;
      Len := StrLen(Source);
      if Len > MaxLen then
        Len := MaxLen;
      Move(Source^, Dest^, Len * SizeOf(WideChar));
      Dest[Len] := #0;
    end;

    Can you see the problem?  It is the call to StrLen(Source)!  In my case the Source was very very long strings, the length of which was unnecessarily calculated the hard way.    The lesson here is that you need to identify the bottlenecks by measuring.  You may be in for surprises, sometimes pleasant ones,
     

     

    • Like 9

  2. 28 minutes ago, Uwe Raabe said:

    They are static by design. The why I cannot answer. Unfortunately there is nothing we can do about it.

    This looks like an inconsistency in the language with regard to class methods and class properties.  And I don't think it is documented.

     


  3. 3 hours ago, Uwe Raabe said:

    Class properties are static

    You access class properties through a class or an instance reference.  That reference could be passed to a virtual class getter. Why do they have to be static?  This is my question.

    A related question.  How do virtual class methods work? Is there an equivalent to VMT for classes?

     

    By the way this works correctly:

    class Test
    protected
      class function GetValue: string; virtual;
    public
      property Value: string read GetValue;
    end;

    i.e. a normal property with a virtual class getter


  4. I tried to do something like

    class Test
    protected
      class function GetValue: string; virtual;
    public
      class property Value: string read GetValue;
    end;
    

    and I got a compiler error message stating the class property read specifier has to be either a class var or a static class method.   Any idea about the reason for this restriction?


  5. 1 hour ago, Stefan Glienke said:

    Could be DirectWrite related if you have a rather poor onboard GPU. Check CPU and GPU usage when it's lagging.

    This is correct, but this is the reason GPU support is disabled by default:

     

          D2D1RenderTargetProperties(
            {$IFDEF GPUSupport}
            D2D1_RENDER_TARGET_TYPE_DEFAULT,
            {$ELSE}
            D2D1_RENDER_TARGET_TYPE_SOFTWARE, // much faster in my desktop with a slow GPU
            {$ENDIF}


    As far as CPU utilization goes, I have tried with a 10K lines pascal file with highlighting, code folding and indentation lines.   Scrolling by pressing continuously the Page Down key results in CPU utiliization around 5% (I7 12700).

    With GPUSupport enabled (currently incompatible with Gutter.ShowLineNumbers - will be fixed), CPU utilization remains close to 0% but GPU utilization rises to 5% (Intel UHD 770).


  6. I will ask Embarcadero to update the Getit packages once the current versions is thoroughly tested and optimized, probably in a few weeks time. 

    But the latency you mentioned is always going to be there, since the development is currently very active.  So if you want the latest fixes and improvements do use the Github version.


  7. 17 hours ago, dados said:

    nd the first thing I do is scroll up and down (page up/down and with mouse dragging vertical scrollbar) ......and the scrolling is lagging/studdering

    This is not the experience here and in the other testers.  Performance with highlighted 10000s of lines is very good.   Could you please submit an issue at the Issue Tracker with sample project and text file?


  8. There were many options mentioned in this thread, but there was no mention of the most obvious one at least on Windows: the built-in Windows spellchecker available since Windows 8.  There many advantages compared to the options discussed here.

    • It is free.
    • Very easy to use.
    • Minimal code to add to your project.
    • No need to distribute dictionaries.  If the user wants a given language dictionary they can get it through Windows language settings.
    • It persists words added, ignored and autocorrected.
    • It detects duplicate words.

    I got the idea from Ian Boyed's answer in this Stackoverflow question, but I could not find a Delphi translation of the Windows Spellcheck API.  So I created my own.   It is included in the attached zip file, along with a demo program.

     

    image.thumb.png.71b6adb9e8eed0d12460dcad8ea8fdb4.png

    SpellCheck.zip

    • Like 1
    • Thanks 5

  9. 17 minutes ago, Bob Devine said:

    Does the code re-factoring make an FMX port more achievable or is that still out of scope?

    I suppose it does a bit.  FMX is using DirectWrite on Windows, and its graphics engine matches the constructs of DirectX (FMX.Canvas.D2D).  One would have to create a VCL abstraction of DirectX that it is similar (possibly identical) to that of FMX.  It could be based on Vcl's Direct2DCanvas.  That would be useful in porting not just SynEdit but other components from Vcl to FMX and the opposite.

    • Like 3

  10. Please see these earlier posts on SynEdit history:

     

    DirectWrite and Unicode support

    One of the major flaws of SynEdit was the poor handling of Unicode.  A major update has been committed to the TurboPack fork, that employs DirectWrite for text painting and fixes Unicode support.  SynEdit should now be on a par with, if not better than, the best editors around with respect to Unicode handling.  For example:

     

    Chinese is properly spaced and surrogate pairs and color emojis are fully supported:

     

    150265824-cbf6652a-bbbc-457c-9498-5f09f9b60423.png.716821f4d6394eec5b41287a68380210.png

     

    Bidirectional text editing is fully supported as well:

     

    150266107-fdccabd2-c766-4ec3-a674-13abdf91e63b.png.fca55cb41c3776b50917149cf3ff988d.png

     

    WordWrap has been re-written and is now based on DirectWrite as well.  This last update also includes other enhancements as for example an option to alpha blend the selection, another option for selection to cover just selected text instead of full lines, as in VS code and other editors, and horizontal mouse wheel scrolling:

     

    152258895-02cc0ccf-dd46-417b-9311-67f5965b52d3.png.fb94e2d33bb73cd762179fb24fec198a.png

     

    Other recent improvements:

    • The undo/redo system was buggy and a mess, getting in the way of implementing new features.  I has been reimplemented from scratch.
    • The gutter has been reimplemented from scratch and is now flexible and extensible.
    • A track changes bar like in Visual Studio has been added and unlike Delphi's it saves and restores line state correctly on undo/redo.
    • The code base has been refactored cleaned-up, and partially documented, yet, and despite of the new features, it is thousands of lines shorter than the original.  But a lot more can be done in this area.
    • See here for the full list of enhancements in the the TurboPack fork.

     

    image.png.6c0a740e35d3f3a818ba6043ed00c312.png

      

    Backward compatibility

    Turbopack Synedit remains compatible with earlier versions of Synedit, but you do need to reinstall Synedit, load forms that use SynEdit ignoring error messages and save them again for the new properties to take effect. The use of DirectWrite though means that Windows XP is no longer supported.  The TurboPack SynEdit fork supports Delphi versions Berlin or later.

     

    Future plans

    The next big planned feature is multi-selection and multi-cursor editing.

     

    Support the project

    Most of the bugs remaining in the issue tracker are C++Builder related.   Also, the C++ packages have not been updated yet.   We are actively seeking contributions on the C++Builder side of things (package updates, bug fixes).

    Of course you can also support the project by submitting bug reports and pull requests.  Or, by implementing new features (e.g. minimap, Sync Edit like in Delphi,  Delphi or VS-code like templates etc.)

     

    Note:  Many thanks to @MarkShark for his great contributions to the SynEdit project.

     

     

     

     

     

    • Like 14
    • Thanks 9

  11. On 9/12/2020 at 8:01 PM, FPiette said:

    Thanks. This is Direct2D 1.0 code that already use. I see you get a DeviceContext when you need it to render the SVG document.

    So you are not following at all what all the articles about Direct2D 1.1 explain. I wonder where the drawback is... Future will tell...

     

    It has been a while, but did you get a better understanding of the differences in the two approaches and the reasons for the slowness?


  12. 7 hours ago, hsauro said:

     

    I assume the TStringList is reference-counted at the python

    Wrong assumption.  You need to call Free on the Python side or free it from Delphi.   You can examine the ownership of a Delphi object in Python using the __owned__ attribute.  If __owned__ is true it will be freed automatically.

     

    When you wrap objects yourself using DelphiWrappper.Wrap, you can specify the ownership.   But here you are returning the object as a function result and you cannot tell who owns the object.   Objects returned as function results or by accessing properties are wrapped using Wrap(obj, soReference);

     

×