Expensive-to-Read Objects in Bug Finder
Polyspace® Bug Finder™ reports several defects when your code performs an expensive read operation that can instead be replaced by a more efficient read operation. When evaluating the efficiency of a read operation, Polyspace considers the costs of reading an object by copy and by dereferencing. An object can be more expensive to read by copy than by dereferencing, and vice versa. This topic clarifies when Bug Finder considers a copy operation to be expensive and when Bug Finder considers a dereferencing operation to be expensive.
Objects That Are Expensive to Read by Copy
During a read operation, Polyspace considers an object to be expensive to read by copy compared to dereferencing if either of these conditions are true:
The object is not a trivially copy-constructible type. A trivially copy-constructible type has a trivial copy constructor and a trivial destructor. To find out if the type of your object is trivially copy-constructible, use the C++ type trait
std::is_trivially_copy_constructible
.The object is trivially copy-constructible but its size is greater than twice the size of a
void*
pointer:sizeof(object) > 2 × sizeof(void*)
Examples of objects that are expensive to read by copy include
std::string
objects, user-defined classes that are not
trivially copy-constructible, most sequence containers such as
std::vector
, all associative containers such as
std::map
, and all unordered associative containers such as
std::unordered_map
.
Consider this code:
#include <vector>
extern void foo(std::vector<int> v);
extern void bar(const std::vector<int> &v);
void by_value(const std::vector<int> &v)
{
foo(v);
}
void by_ref(const std::vector<int> &v)
{
bar(v);
}
Both by_value()
and by_ref()
pass an
std::vector
object to a function. A copy of the vector is
passed by to foo()
while a reference to the vector is passed to
bar()
. Vector objects are not trivially copyable and their
sizes are larger than 2 × sizeof(void*)
. In a typical toolchain,
the passing the vector by value takes 18 instructions, including five
call
instructions. Passing the vector by reference takes five
instructions with only one call
instruction. Polyspace considers vector objects expensive-to-read by copy.
Objects That Are Expensive to Read by Dereference
During a read operation, Polyspace considers an object to be expensive to read by dereference compared to copying if both of these conditions are true:
The object has a trivially copy-constructible type.
The size of the object is less than or equal to
2 × sizeof(void*)
Typical examples of objects that are expensive to read by dereference include plain integral types of various sizes. For example, consider this code:
#include <cstddef>
size_t double_by_ref(size_t const &r)
{
return 2 * r;
}
size_t double_by_value(size_t const r)
{
return 2 * r;
}
Instructions for
double_by_ref()
Instructions for
double_by_value()
The function double_by_ref()
accepts a
size_t
object by reference while the function
double_by_value()
accepts a size_t
object
by value. Objects of type size_t
are trivially-copyable and have
size less than or equal to 2 × sizeof(void*)
. In a typical
toolchain, the function double_by_ref()
takes at least one more
instruction than the function double_by_value()
. Polyspace considers integral types expensive-to-read by dereference.