Why does initializing class variables using std::numeric_limits take so long?

138 Views Asked by At

I have a C++ class which gets instantiated a very large number of times. It has an unsigned short private member. I am getting very different runtime performance depending on whether I initialize it with a hard-coded value 65535 vs when I initialize it with std::numeric_limits<unsigned short>::max(), even though they are ostensibly the same value.

I am trying to understand why there is such a large performance difference, as I would have thought these would be equivalent after compilation. Is it just bad practice to initialize using std::numeric_limits?

Minimal working example below (I've included no initialization for reference):

main.cpp

#include <ostream>
#include <iostream>
#include <ctime>
#include <vector>
#include <limits>

#define TYPE unsigned short
const TYPE max = 65535; // value returned by std::numeric_limits<unsigned short>::max();

class numeric_test_no_init
{
    public:
        numeric_test_no_init() noexcept
        {
            return;
        }
    private:
        TYPE _l;
};

class numeric_test_zero_init
{
    public:
        numeric_test_zero_init() noexcept
        {
            _l=max;
        }
    private:
        TYPE _l;
};

class numeric_test_max_init
{
    public:
        numeric_test_max_init() noexcept
        {
            _l=std::numeric_limits<TYPE>::max();
        }
    private:
        TYPE _l;
};

int main() {
    
    size_t inner_size = 10000000;
    size_t outer_size = 1000;
    clock_t begin, end;
    double elapsed_secs;

    begin = clock();
    for (size_t i=0;i<outer_size;++i)
        std::vector<numeric_test_no_init> no_init(inner_size);
    end = clock();
    elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
    std::cout << "numeric_test_no_init took " << elapsed_secs << " seconds." << std::endl;
    
    begin = clock();
    for (size_t i=0;i<outer_size;++i)
        std::vector<numeric_test_zero_init> zero_init(inner_size);
    end = clock();
    elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
    std::cout << "numeric_test_zero_init took " << elapsed_secs << " seconds." << std::endl;

    begin = clock();
    for (size_t i=0;i<outer_size;++i)
        std::vector<numeric_test_max_init> max_init(inner_size);
    end = clock();
    elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
    std::cout << "numeric_test_max_init took " << elapsed_secs << " seconds." << std::endl;

    return 0;
}

Output:

numeric_test_no_init took 0.003331 seconds.
numeric_test_zero_init took 2.12012 seconds.
numeric_test_max_init took 16.6588 seconds.

I am compiling using GCC 11.4.0 (default for Ubuntu 22.04) with optimization enabled -03.

Update: the performance discrepancy goes away when I upgrade GCC to 13.2. Thank you @retired-ninja for the link demonstrating that.

0

There are 0 best solutions below