Skip to content

String::new() is slower than "".to_string()  #86106

Closed

Description

I tried this code:

pub fn a() -> String {
    String::new()
}

pub fn b() -> String {
    "".to_string()
}

I expected to see this happen: both functions contain identical code.

Instead, this happened: https://rust.godbolt.org/z/P53MMG4W6.

For String::new() it does one more instruction: mov rcx, qword ptr [rip + .L__unnamed_1]. This rcx is then read further below at mov qword ptr [rdi], rcx.
The "".to_string() version however does not do this extra step and immediately reads from a constant: mov qword ptr [rdi], 1.
This means that String::new() does slightly more work than "".to_string() but I expect them to be identical.

Before 1.52.0 the functions generated identical code: https://rust.godbolt.org/z/8647E6YhY.

oli noted the following:

The problem is probably because it copies its value from the constant

pub const NEW: Self = Self::new();
instead of immediately creating the appropriate value

Meta

This currently happens on nightly.

Activity

  1. ghost added
    C-bugCategory: This is a bug.
    on Jun 7, 2021
  2. added
    I-slowIssue: Problems and improvements with respect to performance of generated code.
    on Jun 7, 2021
  3. added
    regression-untriagedUntriaged performance or correctness regression.
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jun 7, 2021
  4. JohnTitor commented on Jun 7, 2021

    @JohnTitor
    Member

    Regressed since 1.52.0, between nightly-2021-03-04 and nightly-2021-03-05 so I assume it's caused (revealed?) by #81451.

  5. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    and removed
    regression-untriagedUntriaged performance or correctness regression.
    on Jun 7, 2021
  6. cynecx commented on Jun 7, 2021

    @cynecx
    Contributor

    Reduced llvm-ir:

    target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
    target triple = "x86_64-unknown-linux-gnu"
    
    %"std::string::String" = type { [0 x i64], %"std::vec::Vec<u8>", [0 x i64] }
    %"std::vec::Vec<u8>" = type { [0 x i64], { i8*, i64 }, [0 x i64], i64, [0 x i64] }
    
    @0 = private unnamed_addr constant <{ [16 x i8] }> <{ [16 x i8] c"\01\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" }>, align 8
    
    define void @abc(%"std::string::String"* %0) unnamed_addr {
    start:
      %1 = load i8*, i8** bitcast (<{ [16 x i8] }>* @0 to i8**), align 8
      %ptr = bitcast %"std::string::String"* %0 to i8**
      store i8* %1, i8** %ptr, align 8
      ret void
    }
    
    ; This works by replacing the pointer type with an integral type
    
    define void @cba(%"std::string::String"* %0) unnamed_addr {
    start:
      %1 = load i64, i64* bitcast (<{ [16 x i8] }>* @0 to i64*), align 8
      %ptr = bitcast %"std::string::String"* %0 to i64*
      store i64 %1, i64* %ptr, align 8
      ret void
    }
    

    LLVM fails to figure out that the load can be replaced with a constant (Probably because of the load bitcast pattern for pointer types).

  7. klensy commented on Jun 7, 2021

    @klensy
    Contributor

    It isn't only in String main thing there in const. The same can be seen in vec:

    pub fn foo()->Vec<u8>{
        vec![]
    }
    
    pub fn foo()->Vec<u8>{
        vec![].clone()
    }
    example::foo:
            mov     rax, rdi
            mov     rcx, qword ptr [rip + .L__unnamed_1]
            mov     qword ptr [rdi], rcx
            xorps   xmm0, xmm0
            movups  xmmword ptr [rdi + 8], xmm0
            ret
    
    .L__unnamed_1:
            .asciz  "\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
    
    
    example::foo:
            mov     rax, rdi
            mov     qword ptr [rdi], 1
            xorps   xmm0, xmm0
            movups  xmmword ptr [rdi + 8], xmm0
            ret
  8. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jun 8, 2021
  9. apiraino commented on Jun 8, 2021

    @apiraino
    Contributor

    Assigning priority as discussed in the Zulip thread of the Prioritization Working Group.

    @rustbot label -I-prioritize +P-high

  10. added
    P-highHigh priority
    and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jun 8, 2021
  11. 6 remaining items

  12. self-assigned this
    on Jan 20, 2022
  13. nikic commented on Jan 21, 2022

    @nikic
    Contributor

    Fixed upstream by llvm/llvm-project@6a19cb8.

  14. SaadiSave commented on Jan 21, 2022

    @SaadiSave

    Fixed upstream by llvm/llvm-project@6a19cb8.

    I assume that this will make its way into Rust with LLVM 14? Or will it become a patch release?

  15. nikic commented on Jan 21, 2022

    @nikic
    Contributor

    Fixed upstream by llvm/llvm-project@6a19cb8.

    I assume that this will make its way into Rust with LLVM 14? Or will it become a patch release?

    This will be part of the LLVM 14 upgrade.

  16. klensy commented on Feb 19, 2022

    @klensy
    Contributor

    Fixed by #93577 https://rust.godbolt.org/z/vMdfPzfT5, but test should be added, to check regressions in future.

  17. added
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    and removed
    P-highHigh priority
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    on Feb 19, 2022
  18. removed their assignment
    on Feb 19, 2022
  19. added a commit that references this issue on Dec 31, 2022
  20. added a commit that references this issue on Jan 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-bugCategory: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.I-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions