Skip to content

Should internal vectors and structs be passed by reference? #530

@TysonRayJones

Description

@TysonRayJones

In QuEST v4, a user-given list of qubits is copied into a std::vector<int> which is passed down through ~10 functions before finally being processed by the backend. Currently each receiving function signature looks like:

void f(vector<int> list) {
    ...
}

and ergo receives a copy of list. This means called functions can safely modify their argument list (which is done frequently, e.g. sorting and updating elements) without corrupting the caller's copy. This is defensively designed, and makes a nice, concise signature.

This does however invoke superfluous copies. Though the lists are small (<64 elements) such that each copy is fast and negligible compared to the runtime of the subsequent statevector simulation, there are many copies invoked between the user's call and the backend simulation. I would estimate ~20 due to all the validation calls, etc. For decreasing number of qubits in the state, the relative cost of the copies vs the backend simulation increases (but not hugely; of course the lists themselves shrink with decreasing qubits).

An alternative approach is to pass by const reference, so that internal function signatures resemble

void f(const vector<int>& list) {
    ...
}

These involves no copying, and the use of const ensures the callee cannot mutate and corrupt the caller's list. The few functions in the call chain that need to modify the list would explicitly instantiate their own copy (a small boilerplate evil of copy = list). It is my understanding a compiler cannot automatically avoid the copying between translation units since it cannot know whether an external function mutates (that's a point in Julia's favour).

Alas, this solution is ugly to me! It adds 6 new characters in every internal function signature which receives a vector, of which there are hundreds. While "receiving a const reference" is standard C++ practise, one could argue it is not essential here given the relative size of the lists.

So: do the evils of superfluous copies outweigh the added boilerplate of const-refs? Is this a premature optimisation, or encouraged standard C++ practise? Can compilers miraculously optimise away copies and make this consideration moot through some magical mechanism??

A similar consideration can be made for structs, like Qureg and CompMatr which are also presently passed-by-copy. These are rather small and fixed-size, so their copy penalties are negligible, but could be made references for consistency with vectors if we changed those to refs.

I welcome any and all thoughts!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions