Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 02/05/21 in all areas

  1. {$APPTYPE CONSOLE} uses System.SysUtils, System.Diagnostics, System.StrUtils; function MyStartsWith(const SearchText, Text: string): Boolean; var Index, SearchTextLen: Integer; begin SearchTextLen := Length(SearchText); if SearchTextLen>Length(Text) then begin Result := False; Exit; end; for Index := 1 to SearchTextLen do if Text[Index]<>SearchText[Index] then begin Result := False; Exit; end; Result := True; end; const cMaxLoop = 10000000; cText = 'Error: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'; cTextNoHits = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'; cStartsWithShort = 'E'; cStartsWithLong = 'Error:'; var vSW: TStopWatch; i, vLen: integer; hitCount: Integer; vResult: boolean; begin Writeln('Substring exists at the start:'); // Short string Writeln('Short string:'); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if Copy(cText, 1, Length(cStartsWithShort)) = cStartsWithShort then Inc(hitCount); Writeln(' Copy: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MidStr(cText, 1, Length(cStartsWithShort)) = cStartsWithShort then Inc(hitCount); Writeln(' MidStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if cText.StartsWith(cStartsWithShort) then Inc(hitCount); Writeln(' StartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if StartsStr(cStartsWithShort, cText) then Inc(hitCount); Writeln(' StartsStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MyStartsWith(cStartsWithShort, cText) then Inc(hitCount); Writeln(' MyStartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); // Long string Writeln('Long string:'); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if Copy(cText, 1, Length(cStartsWithLong)) = cStartsWithLong then Inc(hitCount); Writeln(' Copy: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MidStr(cText, 1, Length(cStartsWithLong)) = cStartsWithLong then Inc(hitCount); Writeln(' MidStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if cText.StartsWith(cStartsWithLong) then Inc(hitCount); Writeln(' StartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if StartsStr(cStartsWithLong, cText) then Inc(hitCount); Writeln(' StartsStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MyStartsWith(cStartsWithLong, cText) then Inc(hitCount); Writeln(' MyStartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); Writeln; // Text DEOS NOT start with selected string Writeln('Substring NOT exists at the start:'); // Short string Writeln('Short string:'); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if Copy(cTextNoHits, 1, Length(cStartsWithShort)) = cStartsWithShort then Inc(hitCount); Writeln(' Copy: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MidStr(cTextNoHits, 1, Length(cStartsWithShort)) = cStartsWithShort then Inc(hitCount); Writeln(' MidStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if cTextNoHits.StartsWith(cStartsWithShort) then Inc(hitCount); Writeln(' StartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if StartsStr(cStartsWithShort, cTextNoHits) then Inc(hitCount); Writeln(' StartsStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MyStartsWith(cStartsWithShort, cTextNoHits) then Inc(hitCount); Writeln(' MyStartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); // Long string Writeln('Long string:'); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if Copy(cTextNoHits, 1, Length(cStartsWithLong)) = cStartsWithLong then Inc(hitCount); Writeln(' Copy: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MidStr(cTextNoHits, 1, Length(cStartsWithLong)) = cStartsWithLong then Inc(hitCount); Writeln(' MidStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if cTextNoHits.StartsWith(cStartsWithLong) then Inc(hitCount); Writeln(' StartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if StartsStr(cStartsWithLong, cTextNoHits) then Inc(hitCount); Writeln(' StartsStr: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); hitCount := 0; vSW := TStopWatch.StartNew; for i := 1 to cMaxLoop do if MyStartsWith(cStartsWithLong, cTextNoHits) then Inc(hitCount); Writeln(' MyStartsWith: ' + vSW.ElapsedMilliseconds.ToString, ' ', hitCount); readln; end. Copy is clearly a shocker of an idea for this use. heap allocation!! Really?!!! And the other functions seem really slow. A simple for loop is around 10 times faster. I didn't capture any timings, but it's easy to do it on your machine. I also addressed a couple of issues with your benchmark code. I don't believe this is a bottleneck in your program, but you love premature optimisation with a rarely seen passion.
  2. FPiette

    Delphi book

    There is a new Delphi book in FRENCH. It will be useful to all people wanting to learn object oriented programming using Delphi. The books is based on Delphi 10.3 Community Edition but of course is also very good for all other editions and recent versions. To be noted: the author make use of UML schemas to illustrate OOP. The book is mostly oriented to Windows with VCL but also includes two chapters introducing FMX. https://www.editions-eni.fr/livre/delphi-10-3-programmation-orientee-objet-en-environnement-windows-9782409024665
  3. Really? Ouch 😨 It used to be much simpler: function TStringHelper.StartsWith(const Value: string): Boolean; begin Result := StartsWith(Value, False); end; function TStringHelper.StartsWith(const Value: string; IgnoreCase: Boolean): Boolean; begin if not IgnoreCase then Result := System.SysUtils.StrLComp(PChar(Self), PChar(Value), Value.Length) = 0 else Result := System.SysUtils.StrLIComp(PChar(Self), PChar(Value), Value.Length) = 0; end; function StrLComp(const Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; var I: Cardinal; P1, P2: PWideChar; begin P1 := Str1; P2 := Str2; I := 0; while I < MaxLen do begin if (P1^ <> P2^) or (P1^ = #0) then Exit(Ord(P1^) - Ord(P2^)); Inc(P1); Inc(P2); Inc(I); end; Result := 0; end; function StrLIComp(const Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; var P1, P2: PWideChar; I: Cardinal; C1, C2: WideChar; begin P1 := Str1; P2 := Str2; I := 0; while I < MaxLen do begin if P1^ in ['a'..'z'] then C1 := WideChar(Word(P1^) xor $20) else C1 := P1^; if P2^ in ['a'..'z'] then C2 := WideChar(Word(P2^) xor $20) else C2 := P2^; if (C1 <> C2) or (C1 = #0) then Exit(Ord(C1) - Ord(C2)); Inc(P1); Inc(P2); Inc(I); end; Result := 0; end; Compared to StartsStr(), which (eventually) calls CompareString(): function StartsStr(const ASubText, AText: string): Boolean; begin Result := AnsiStartsStr(ASubText, AText); end; function AnsiStartsStr(const ASubText, AText: string): Boolean; begin Result := AnsiSameStr(ASubText, Copy(AText, 1, Length(ASubText))); // WHY Copy()? AnsiStrLComp() could have been used instead! end; function AnsiSameStr(const S1, S2: string): Boolean; begin Result := AnsiCompareStr(S1, S2) = 0; end; function AnsiCompareStr(const S1, S2: string): Integer; {$IFDEF MSWINDOWS} begin Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1), PChar(S2), Length(S2)) - CSTR_EQUAL; end; {$ENDIF MSWINDOWS} {$IFDEF POSIX} begin Result := UCS4CompareStr(UnicodeStringToUCS4String(S1), UnicodeStringToUCS4String(S2)); end; {$ENDIF POSIX} I don't have RTL/VCL source code past XE3, but I keep seeing people mention how bad it's getting in recent years. This is not good.
  4. Not if the search string is longer than the other string. Then it's a buffer overrun. Use the version from my post.
  5. Things changed since then... for some reason unknown to me StartsWith has been changed to the point it involves 10+ calls to other procedures, and eventually calls Windows API CompareString function. Since the final call handles case insensitive variant, it makes sense in that case (still too many indirect calls for my taste), but code path for case sensitive variant is just WHY, OH, WHY???? No wonder it is slower... I think the plain for loop comparing chars would do better...
  6. I find that hard to believe. But I have no way to test that myself right now. When Copy() or MidStr() are used to return a partial substring of a larger string, they dynamically allocate a new string and copy characters into it. Your tests are requesting smaller substrings. So there should be allocations being performed before any comparisons can be made. That would take more time. The only way those allocations should not be done is when the entire input string is being returned as-is, in which case the reference count for a non-constant gets incremented, and a constant gets returned as-is (no reference counting). This is even worse for StartsStr(), because it also uses Copy() internally, to chop the input string down to the size of the comparison string, and then compares the result to the comparison string. That is really unnecessary, as TStringHelper.StartsWith() proves. TStringHelper.StartsWith() simply calls SysUtils.StrL(I)Comp(), passing it pointers to the input strings, and the max length to compare. It compares the characters directly in the original memory without making any allocations at all. Which is the way it should be done. So I would have expected TStringHelper.StartsWith() to be much faster than the others, not in the middle. But then, I was looking at an older version (XE3). Maybe things have changed in recent years? Any comparison that avoids having to allocate new strings should be the fastest.
  7. Fixed in 10.4.1: Object inspector looses a changed property value when selecting an other component
  8. Rollo62

    Sidney, RestDebuger and REST

    @Serge_Gthanks anyway, so I don't have to look into it too. Yes, that saves a lot of redundant work .... Sorry that you will be grabbed now by Emb-Police, farewell.
  9. Lars Fosdal

    Free EurekaLog license drawing

    Congratulations, @Attila Kovacs 🙂
  10. I had created the report in Quality Central already ( RSP-32335 ). I also attached your Project1.dpr with the report.
  11. Attila Kovacs

    Free EurekaLog license drawing

    Holy cow.
  12. Mike Torrettinni

    Free EurekaLog license drawing

    I guess they forgot to update on results all the forums they posted this. Anybody interested on results: https://blog.eurekalog.com/2021/02/eurekalog-2020-survey-results.html
  13. Angus Robertson

    Mustangpeak UltraExplorer

    The Delphi source code for UltraExplorer is now available in SVN at http://svn.magsys.co.uk:8443/svn/mustangpeak/ username = ics and password = ics for read access. The installable version may be downloaded from https://www.magsys.co.uk/delphi/ultraexplorer.asp Angus
×