Indexing a const. array. Memory performance

I've read that:

  1. consts have usually better performance than statics.
  2. consts values are inserted in text section of the binary each time their used.

But what if my object is big (eg. array, possibly a mapping/decoding table)?
If such big (here 1MB) array gets inserted each time it is indexed with a variable (not constant), (here get function does this), the binary size will be hugely enlarged.

const ARR:[u8;1_048_576]= {
  let mut a= [0;1_048_576];
  a[1]= 8;
  a[7]= 6;
  // ...
  a
};

fn get(ind:usize)->u8{ARR[ind]}

So my intuition would say don't create big const object if their single parts are to be used more than once.
But I don't really know how does compiler resolve indexing, getting member or address of a big const value.

Does anyone know? Should I choose static over const because of size?

None of those two claims are true unconditionally. The compiler will usually try to optimize so that you get the best runtime performance without blowing up memory. It may or may not inline consts. It may or may not inline statics. It doesn't really matter whether you use a const or a static from a performance point of view (since the initializer of a static must also be known at compile time).

No. Choose between them based on what you need them to do.

1 Like

This advice feels wrong to me. The language reference says that consts are regenerated and statics aren't.

So something like this:

const N: usize = 1000000;
const A: [i32; N] = [3; N];

fn main() {
    let mut sum = 0;
    for i in 0..N {
        sum += A[i];
    }
    println!("{sum}");
}

is an O(N²) algorithm in the Rust Abstract Machine, and you're hoping that the optimizer will optimize it to O(N). Whereas if you use static it's already O(N) in the Rust Abstract Machine.

It's true that the above code does get optimized in release mode. But I don't feel comfortable relying on the optimizer to improve the algorithm complexity from O(N²) to O(N).

It's a bit like saying that it doesn't matter whether you implement insertion sort or merge sort, because the optimizer is allowed to convert the code in either direction -- well it is allowed to, but optimizers rarely make the asymptotic complexity worse.

The other thing is, in debug mode the const doesn't get optimized, so it's 1000000x slower in debug mode than with static. So you'll never be able to run unit tests in debug mode if you use const for this.

1 Like

I doubt that is actually what it says, or at the very least, it's probably not the intention. It just feels wrong on a fundamental level to expect that such a pure expression will generate literal code upon every single iteration. I'm also pretty sure the purpose of that description is to warn people not to rely on the stability or uniqueness of the adress of a const.

No, there is a fundamental difference. The code with the const array is trivial to optimize, and it is reasonable to expect it to be optimized.

Anyway, "it's slow in debug mode" is not a good argument against consts anyway, because we rely on release-mode-only optimizations all the time, and even big Ordo complexity aside, they have an enormous impact on performance in practice, even if it's "just" a constant factor. So appealing to asymptotic complexity (especially when the N is a compile-time constant) seems to be a red herring and not convincing to me.

1 Like

Unfortunate that indexing arrays doesn’t involve the Index trait, otherwise, A[i] would desugar to *Index::index(&A, i), and &A would fall under constant promotion (to a &'static [i32; N]) and hence “optimized” so it’s no longer on the stack, even in debug builds.

Here, see how a custom type that implements Index behaves this way, or how using *&A[i] instead of A[i] “saves” the stack, too.


Of course, with constant promotion, the constant can still - in principle - be duplicated once per appearance in the code; regarding optimizations I’m also not sure if there aren’t limits to deduplication of consts, say across crate boundaries, or in case a crate is compiled with multiple codegen units in parallel. I’d say that it does IMO make a lot of sense to use a static once the data is large enough that you want it duplicated under no circumstances whatsoever; the only potential downside is that static is slightly less flexible, e.g. you can’t put a reference to a static into a const, but if these limits don’t limit the use case, going for a static seems like the more sane option to me.

1 Like