Expensive pass by value

Parameter might be expensive to copy

Description

This defect occurs when you pass a parameter by value instead of by reference or pointer, but the parameter is unmodified and either:

  • The parameter is a non-trivially copyable type. For more on non-trivially copyable types, see is_trivially_copyable.

  • The parameter is a trivially copyable type that is above a certain size. For example, an object greater than 2 * sizeof(void *) is more expensive to pass by value than by reference or pointer.

Polyspace® flags unmodified parameters that meet the preceding conditions even if the parameters are not declared const.

Polyspace raises no defect if:

  • The passed by value parameter is a move-only type. For instance, std::unique_ptr can be moved-from but cannot be copied.

  • The passed by value parameter is modified.

For example, no defect is raised in this code where a large trivially copyable Buffer and a move-only unique_ptr are passed by value.

#include<memory>

typedef struct Buffer {
    unsigned char bytes[ 20 ];
} Buffer;


void func1(Buffer modified_param)
{
    ++modified_param.bytes[ 0 ];
}

void func2(std::unique_ptr<Buffer> move_only_param);

Risk

Passing a parameter by value creates a copy of the parameter which is inefficient if the parameter is expensive to copy. Even if your intent is to pass by reference or pointer, you might forget the const& or const* in your function signature and inadvertently run an inefficient version of the function.

Fix

Convert the parameter to a const pointer (const*) for C code, or to a const reference (const&) for C++ code.

Performance improvements might vary based on the compiler, library implementation, and environment that you are using.

Examples

expand all

#include<string>

class Player
{
public:
    void setName(std::string const str)
    {
        name = str;
    }
    void setRank(size_t r)
    {
        rank = r;
    }
    // getter functions implementation
private:
    std::string name;
    size_t rank;

};

In this example, Polyspace flags the parameter of setter function setName which is passed by value and results in an expensive copy. The type std::string is not trivially copyable. The passed by value parameter of setRank is not flagged because size_t is a small trivially copyable type.

Correction — Pass std::string Parameter by const Reference

To avoid an inefficient copy operation, use a const& to pass the parameter.

#include<string>

class Player
{
public:
    void set_name(std::string const& s)
    {
        name = s;
    }
    void set_rank(size_t r)
    {
        rank = r;
    }
    // getter functions implementation
private:
    std::string name;
    size_t rank;

};
#include<stdio.h>
#include<string.h>

typedef struct _Player {
    char name[50];
    size_t rank;
} Player;


void printPlayer(Player const player)
{

    printf("Player name: %s\n", player.name);
    printf("Player rank: %zu\n", player.rank);
}

In this example, Polyspace flags the parameter of printPlayer which is passed by value and results in an expensive copy.

Correction — Pass Large Struct by const Pointer

To avoid an inefficient copy operation, use a const* to pass the parameter, then use the appropriate notation to read the structure elements.

#include<stdio.h>
#include<string.h>

typedef struct _Player {
    char name[50];
    size_t rank;
} Player;


void printPlayer(Player const* player)
{

    printf("Player name: %s\n", player->name);
    printf("Player rank: %zu\n", player->rank);
}

Result Information

Group: Performance
Language: C | C++
Default: Off
Command-Line Syntax: EXPENSIVE_PASS_BY_VALUE
Impact: Medium
Introduced in R2020b