Recent

Author Topic: Unneeded copy of string  (Read 1761 times)

LemonParty

  • Full Member
  • ***
  • Posts: 165
Unneeded copy of string
« on: April 15, 2025, 06:23:14 pm »
Hello.

I have this simple code:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. procedure P1(constref Ch: AnsiChar);
  4. begin
  5.   Writeln(Ch);
  6. end;
  7.  
  8. var
  9.   S: AnsiString = '12345';
  10.  
  11. begin
  12.   P1(S[1]);
  13. end.
  14.  
When I disassebled this code I found this:
Code: ASM  [Select][+][-]
  1. # [32] P1(S[1]);
  2.   leaq  TC_$P$PROGRAM_$$_S(%rip),%rcx
  3.   call  fpc_ansistr_unique
  4.   movq  %rax,%rcx
  5.   call  P$PROGRAM_$$_P1$CHAR
  6.  
The question is why compiler make copy of a string?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

PascalDragon

  • Hero Member
  • *****
  • Posts: 5940
  • Compiler Developer
Re: Unneeded copy of string
« Reply #1 on: April 15, 2025, 11:06:13 pm »
The question is why compiler make copy of a string?

constref enforces the passing of Ch as a reference aka a Pointer. To ensure that another potential reference to the string doesn't interfere with that passed in reference the compiler ensures a unique copy of the string. For a primitive argument type const is better. In general you should prefer to use const because the compiler will pick the most efficient way to pass the parameter - unless you really need a reference to the passed in value.

LemonParty

  • Full Member
  • ***
  • Posts: 165
Re: Unneeded copy of string
« Reply #2 on: April 16, 2025, 10:56:16 am »
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. procedure P1(constref Ch: AnsiChar);
  4. begin
  5.   Writeln('P1 Char');
  6. end;
  7.  
  8. procedure P1(constref Ch: WideChar);
  9. begin
  10.   Writeln('P1 Wide');
  11. end;
  12.  
  13. procedure P2(P: PAnsiChar);
  14. begin
  15.   Writeln('P2 Char');
  16. end;
  17.  
  18. procedure P2(P: PWideChar);
  19. begin
  20.   Writeln('P2 Wide');
  21. end;
  22.  
  23. var
  24.   S: AnsiString = '12345';
  25.   W: WideString = '12345';
  26.  
  27. begin
  28.   P1(S[1]);
  29.   P1(W[1]);
  30.   P2(@S[1]);
  31.   P2(@W[1]);
  32.   Writeln('1');
  33. end.
  34.  
This code demonstrate a problem when use something else than constref. It would be nice if compiler have an option to turn off such hidden copying.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

Thaddy

  • Hero Member
  • *****
  • Posts: 16813
  • Ceterum censeo Trump esse delendam
Re: Unneeded copy of string
« Reply #3 on: April 16, 2025, 11:14:29 am »
well, then make it const as PascalDragon suggested.
Changing servers. thaddy.com may be temporary unreachable but restored when the domain name transfer is done.

LemonParty

  • Full Member
  • ***
  • Posts: 165
Re: Unneeded copy of string
« Reply #4 on: April 16, 2025, 11:22:45 am »
I need a reference.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

Thaddy

  • Hero Member
  • *****
  • Posts: 16813
  • Ceterum censeo Trump esse delendam
Re: Unneeded copy of string
« Reply #5 on: April 16, 2025, 11:45:56 am »
PascalDragon explained that in that case you can't avoid a copy.
That is related to the type being a managed type.
« Last Edit: April 16, 2025, 11:48:09 am by Thaddy »
Changing servers. thaddy.com may be temporary unreachable but restored when the domain name transfer is done.

Khrys

  • Full Member
  • ***
  • Posts: 215
Re: Unneeded copy of string
« Reply #6 on: April 16, 2025, 11:46:28 am »
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. procedure P1(constref Ch: AnsiChar);
  4. begin
  5.   Writeln('P1 Char');
  6. end;
  7.  
  8. procedure P1(constref Ch: WideChar);
  9. begin
  10.   Writeln('P1 Wide');
  11. end;
  12.  
  13. procedure P2(P: PAnsiChar);
  14. begin
  15.   Writeln('P2 Char');
  16. end;
  17.  
  18. procedure P2(P: PWideChar);
  19. begin
  20.   Writeln('P2 Wide');
  21. end;
  22.  
  23. var
  24.   S: AnsiString = '12345';
  25.   W: WideString = '12345';
  26.  
  27. begin
  28.   P1(S[1]);
  29.   P1(W[1]);
  30.   P2(@S[1]);
  31.   P2(@W[1]);
  32.   Writeln('1');
  33. end.
  34.  
This code demonstrate a problem when use something else than constref. It would be nice if compiler have an option to turn off such hidden copying.

This can be solved by using  {$TYPEDADDRESS ON}  aka  {$T+}

Thaddy

  • Hero Member
  • *****
  • Posts: 16813
  • Ceterum censeo Trump esse delendam
Re: Unneeded copy of string
« Reply #7 on: April 16, 2025, 11:53:21 am »
Did you check the assembler output?
Changing servers. thaddy.com may be temporary unreachable but restored when the domain name transfer is done.

Khrys

  • Full Member
  • ***
  • Posts: 215
Re: Unneeded copy of string
« Reply #8 on: April 16, 2025, 01:15:25 pm »
Did you check the assembler output?

I assume the problem @LemonParty was referring to is the compiler selecting the "wrong" overload in line 31 (PWideChar  is supplied, but under  {$T-}  the compiler chooses the  PAnsiChar  version). It's not about string copying.

The output should be  P1 Char, P1 Wide, P2 Char, P2 Wide  but without  {$T+}  it's actually  P1 Char, P1 Wide, P2 Char, P2 Char.
With constref the @ operator could be avoided, preventing loss of type information in that case and allowing the compiler to select the correct overload.

@LemonParty is this the actual problem you were trying to solve? I've answered a similiar question about overloads before (that you posted).

Thaddy

  • Hero Member
  • *****
  • Posts: 16813
  • Ceterum censeo Trump esse delendam
Re: Unneeded copy of string
« Reply #9 on: April 16, 2025, 01:48:28 pm »
In this case we need the assembler output.
Will check when I am home.
Changing servers. thaddy.com may be temporary unreachable but restored when the domain name transfer is done.

LemonParty

  • Full Member
  • ***
  • Posts: 165
Re: Unneeded copy of string
« Reply #10 on: April 16, 2025, 03:19:50 pm »
Quote
This can be solved by using  {$TYPEDADDRESS ON}  aka  {$T+}
Is this going to work when I place procedures in separate unit or whole project should be compiled with this switch?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

beria2

  • New Member
  • *
  • Posts: 16
Re: Unneeded copy of string
« Reply #11 on: April 16, 2025, 03:33:11 pm »
I need a reference.
it is the link that is needed - use only the Pointer type. I always do that. And you always know exactly what you have.

ASerge

  • Hero Member
  • *****
  • Posts: 2404
Re: Unneeded copy of string
« Reply #12 on: April 16, 2025, 04:03:38 pm »
Quote
This can be solved by using  {$TYPEDADDRESS ON}  aka  {$T+}
Is this going to work when I place procedures in separate unit or whole project should be compiled with this switch?
A separate unit is enough.
Interestingly, the discussion turned to another issue, not the one stated in the topic.

Khrys

  • Full Member
  • ***
  • Posts: 215
Re: Unneeded copy of string
« Reply #13 on: April 16, 2025, 04:24:52 pm »
Quote
This can be solved by using  {$TYPEDADDRESS ON}  aka  {$T+}
Is this going to work when I place procedures in separate unit or whole project should be compiled with this switch?

It's a local directive, meaning that it can be toggled at the token level.
You could even enable it just for a single part of an expression if you really wanted to:

Code: Pascal  [Select][+][-]
  1. P2({$push}{$T+} @W[1] {$pop});

Interestingly, the discussion turned to another issue, not the one stated in the topic.

Turns out it was an XY problem after all!   :)

LemonParty

  • Full Member
  • ***
  • Posts: 165
Re: Unneeded copy of string
« Reply #14 on: April 16, 2025, 05:25:49 pm »
I have built a test program
u1.pas:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. {$T+}
  4.  
  5. unit u1;
  6.  
  7. interface
  8.  
  9. procedure P2(P: PAnsiChar);
  10. procedure P2(P: PWideChar);
  11.  
  12. implementation
  13.  
  14. procedure P2(P: PAnsiChar);
  15. begin
  16.   Writeln('P2 Char');
  17. end;
  18.  
  19. procedure P2(P: PWideChar);
  20. begin
  21.   Writeln('P2 Wide');
  22. end;
  23.  
  24. end.

prog.pas:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses u1;
  4.  
  5. var
  6.   S: AnsiString = '12345';
  7.   W: WideString = '12345';
  8.  
  9. begin
  10.   {$Push}{$T+}
  11.   P2(@S[1]);
  12.   P2(@W[1]);
  13.   {$Pop}
  14.   {$Push}{$T-}
  15.   P2(@S[1]);
  16.   P2(@W[1]);
  17.   {$Pop}
  18. end.
Output is:
Quote
P2 Char
P2 Wide
P2 Char
P2 Char
This mean all project should be built with {T+} option. This also mean we can't use overloaded functions in units that rely on strings.
I think behavior of compiler around constref should be changed. As I suppouse idea of constref is that we reference something, but not change it. So compiler should not do copy of a string on constref.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

 

TinyPortal © 2005-2018