Live data from Hacker News

Rust Performance Pitfalls

llogiq.github.io

1–10 of 112 posts

Re: Rust Performance Pitfalls

#2
I'm not a compiler expert, but it seems like some of these should be unnecessary, especially with Rust's strong knowledge of types and ownership lifespans. Like this example from the article:

    let nopes : Vec = bleeps.iter().map(boop).collect();
    let frungies : Vec = nopes.iter().filter(|x| x > MIN_THRESHOLD).collect();
where he recommends avoiding the first collect(). Can't the optimizer do that for you if you don't do anything else with nopes?

Re: Rust Performance Pitfalls

#3

I'm not a compiler expert, but it seems like some of these should be unnecessary, especially with Rust's strong knowledge of types and ownership lifespans. Like this example from the article: let nopes : Vec = bleeps.iter().map(boop).collect(); let frungies : Vec = nopes.iter().filter(|x| x > MIN_THRESHOLD).collect(); where he recommends avoiding the first collect(). Can't the optimizer do that for you if you don't d…

On a high level it seems like it could, but collect() performs heap allocation (and size checks and reallocations for unknown-length iterators), and that's probably too big side effect for LLVM to ignore.

Re: Rust Performance Pitfalls

#4

I'm not a compiler expert, but it seems like some of these should be unnecessary, especially with Rust's strong knowledge of types and ownership lifespans. Like this example from the article: let nopes : Vec = bleeps.iter().map(boop).collect(); let frungies : Vec = nopes.iter().filter(|x| x > MIN_THRESHOLD).collect(); where he recommends avoiding the first collect(). Can't the optimizer do that for you if you don't d…

Good question. That would mean chaining two statements together so "nopes" is not allocated as a symbol. But, with "let", the user is explicitly instructing the compiler to allocate a reference for "nopes", so i don't think the compiler would chain the statements. Unless it takes the task to checking that "nopes" is not used elsewhere.

Re: Rust Performance Pitfalls

#5

I'm not a compiler expert, but it seems like some of these should be unnecessary, especially with Rust's strong knowledge of types and ownership lifespans. Like this example from the article: let nopes : Vec = bleeps.iter().map(boop).collect(); let frungies : Vec = nopes.iter().filter(|x| x > MIN_THRESHOLD).collect(); where he recommends avoiding the first collect(). Can't the optimizer do that for you if you don't d…

There might be side effects from the first call to `collect`, so the compiler can't get rid of it without potentially changing the semantics of the program.

Re: Rust Performance Pitfalls

#6
post #3

I'm not a compiler expert, but it seems like some of these should be unnecessary, especially with Rust's strong knowledge of types and ownership lifespans. Like this example from the article: let nopes : Vec = bleeps.iter().map(boop).collect(); let frungies : Vec = nopes.iter().filter(|x| x > MIN_THRESHOLD).collect(); where he recommends avoiding the first collect(). Can't the optimizer do that for you if you don't d…

On a high level it seems like it could, but collect() performs heap allocation (and size checks and reallocations for unknown-length iterators), and that's probably too big side effect for LLVM to ignore.

Does current Rust contain a way to mark functions as Pure?

Re: Rust Performance Pitfalls

#7
> To get rid of the checks, we can either use bytes directly (usually via Vec / &[u8]) or, if we are absolutely sure the input will be valid UTF-8, use str::from_utf8_unchecked(_) (note that this will require unsafe and break your code in surprising ways should the input not be valid UTF-8).

I believe this needs a stronger warning. Functions that operate on strings are allowed to assume that their input is valid UTF-8 and may perform out-of-bounds memory accesses if the input is not valid UTF-8. Therefore, creating a string containing invalid UTF-8 can lead to a memory safety vulnerability. The suggestion to use this function seems out-of-place in this article, which otherwise avoids suggesting unsafe code (e.g. indexing arrays without bounds checking).

Re: Rust Performance Pitfalls

#8
post #4

I'm not a compiler expert, but it seems like some of these should be unnecessary, especially with Rust's strong knowledge of types and ownership lifespans. Like this example from the article: let nopes : Vec = bleeps.iter().map(boop).collect(); let frungies : Vec = nopes.iter().filter(|x| x > MIN_THRESHOLD).collect(); where he recommends avoiding the first collect(). Can't the optimizer do that for you if you don't d…

Good question. That would mean chaining two statements together so "nopes" is not allocated as a symbol. But, with "let", the user is explicitly instructing the compiler to allocate a reference for "nopes", so i don't think the compiler would chain the statements. Unless it takes the task to checking that "nopes" is not used elsewhere.

Compiler backends don't particularly care about the user-defined variables in the source, since a typical compilation step is to convert to SSA form ( https://en.wikipedia.org/wiki/Static_single_assignment_form ). And it's relatively easy for a backend to remove allocations (or e.g. ignore the act of taking a reference) for variables that never get used. As long as the semantics are preserved, it's all good. The problem in this case is that removing implicitly that first call to collect might change the semantics of the program.

Re: Rust Performance Pitfalls

#9
post #3

Earlier quoted context omitted.

On a high level it seems like it could, but collect() performs heap allocation (and size checks and reallocations for unknown-length iterators), and that's probably too big side effect for LLVM to ignore.

Does current Rust contain a way to mark functions as Pure?

Not in the referentially-transparent sense. Most Rust functions meet almost all of the practical criteria for for purity (i.e. does not mutate global state (or otherwise any state that was not explicitly passed in to the function), does not do I/O), but that's only a comfort for the programmer's ability to reason about the code; this weakened notion of purity-by-default isn't enough to allow the typical optimizations via purity (e.g. memoization).

Re: Rust Performance Pitfalls

#10
post #7

> To get rid of the checks, we can either use bytes directly (usually via Vec / &[u8]) or, if we are absolutely sure the input will be valid UTF-8, use str::from_utf8_unchecked(_) (note that this will require unsafe and break your code in surprising ways should the input not be valid UTF-8). I believe this needs a stronger warning. Functions that operate on strings are allowed to assume that their input is valid UTF-…

What sort of language beyond "absolutely sure", "require unsafe", and "break your code in surprising ways" would you recommend to make this warning stronger?
Post reply on HN