Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up"".to_string() is slower than String::new() #18404
Comments
This comment has been minimized.
This comment has been minimized.
|
It also allocates a lot of memory (128 bytes minimum) even for small / empty strings. |
thestinger
added
I-slow
A-libs
labels
Oct 28, 2014
barosl
referenced this issue
Dec 2, 2014
Merged
Improve type safety of serialize::json::Encoder #19249
This comment has been minimized.
This comment has been minimized.
|
This appears to be fixed. |
This comment has been minimized.
This comment has been minimized.
|
Only the capacity part of it. The code that creates the String instance is still awful. Apparently there's more inlining going on now, so the code for pub fn from_str() -> String {
String::from_str("")
} .section .text._ZN8from_str20h167891935c11e273laaE,"ax",@progbits
.globl _ZN8from_str20h167891935c11e273laaE
.align 16, 0x90
.type _ZN8from_str20h167891935c11e273laaE,@function
_ZN8from_str20h167891935c11e273laaE:
.cfi_startproc
movq $1, (%rdi)
movq $0, 16(%rdi)
movq $0, 8(%rdi)
movq %rdi, %rax
retq
.Ltmp44:
.size _ZN8from_str20h167891935c11e273laaE, .Ltmp44-_ZN8from_str20h167891935c11e273laaE
.cfi_endprocOTOH, this pub fn to_string() -> String {
"".to_string()
}gives you almost 200 lines of asm (including labels), and that still calls into |
This comment has been minimized.
This comment has been minimized.
extern crate test;
use test::{Bencher, black_box};
#[bench]
pub fn to_string(b: &mut Bencher) {
b.iter(|| {
black_box("".to_string());
});
}
#[bench]
pub fn from_str(b: &mut Bencher) {
b.iter(|| {
black_box(String::from_str(""));
});
}
|
This comment has been minimized.
This comment has been minimized.
|
A naive micro-benchmark won't be measuring the cost of reallocations. In the real world, it will rarely have room to expand in-place every time. |
This comment has been minimized.
This comment has been minimized.
|
Neither of those should allocate. You get a String without any allocated capacity in both cases. |
This comment has been minimized.
This comment has been minimized.
|
I'm aware. I'm pointing it out in advance so it can be done properly, because I'm sure measuring the cost of the repeated reallocations is going to be done too. |
kmcallister
added
the
A-collections
label
Jan 16, 2015
This comment has been minimized.
This comment has been minimized.
|
What is the preferred way to do this these days? servo/servo#4682 is changing Servo to use |
This comment has been minimized.
This comment has been minimized.
|
Note that we can't just special case I have another idea though. How about we implement That won't solve the problem of allocating too much, but it might lessen the code bloat a bit. |
This comment has been minimized.
This comment has been minimized.
|
After some thought, I came up with another, simpler, idea. First, add a method to pub trait Display {
// ...
#[inline]
fn as_str_slice(&self) -> Option<&str> { None }
}This method will return impl Display for str {
// ...
#[inline]
fn as_str_slice(&self) -> Option<&str> { Some(self) }
}Now, here's where the magic lies. In impl<T: Display + ?Sized> ToString for T {
fn to_string(&self) -> String {
match self.as_str_slice() {
Some(s) => s.to_owned(),
None => // as before
}
}
}Since Thoughts? I guess it's a bit icky having an extra method there, but if we hide it from the docs it should be unobtrusive enough. |
lfairy
referenced this issue
Jan 29, 2015
Open
Can't write non-overlapping blanket impls that involve associated type bindings #20400
This comment has been minimized.
This comment has been minimized.
|
Okay, so I prototyped my idea. Initial benchmarks look very promising, with There's a problem though with smart pointer types, specifically This decision should really be made on the type level, using either associated types or negative bounds. Unfortunately the associated types solution doesn't work yet (#20400), and negative bounds are still in the pipeline (rust-lang/rfcs#586). So I guess we'll have to wait. |
This comment has been minimized.
This comment has been minimized.
Borrowing a pointer out of an |
This comment has been minimized.
This comment has been minimized.
Ah, I stand corrected! That makes my solution practical, though it still feels like a hack. I guess if we treat this as a temporary solution, to be removed when negative bounds land, it should be okay. |
cmr
self-assigned this
Mar 25, 2015
ddrmanxbxfr
referenced this issue
Oct 18, 2015
Merged
to_owned should be used in favor of to_string #29
cmr
removed their assignment
Jan 5, 2016
This comment has been minimized.
This comment has been minimized.
|
cc me |
This comment has been minimized.
This comment has been minimized.
|
Is this still relevant/reproducible? |
This comment has been minimized.
This comment has been minimized.
|
Specialization has landed. This can be worked on now? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Was this effectively fixed by #32586? |
This comment has been minimized.
This comment has been minimized.
|
It should have been, yes! |
cgaebel commentedOct 28, 2014
In a lot of rust code, I see
"".to_string()being used as the default method of creating a new string. This would normally be fine, but it's actually noticeably slower thanString::new()because it goes through the formatting infrastructure.It'd be nice if this pattern could be special-cased in
String::to_string's implementation, or the optimizer made to do this transformation better.