Return static vector by reference is slow
Asked Answered
M

1

5

I was setting up a cache to draw some shapes. My approach was as following:

I created a class OrbitCacheManager.h that looked like this:

#ifndef OrbitCacheManager_h
#define OrbitCacheManager_h
#include <vector>
#include "cache_0_0.h"
#include "cache_1_0.h"
// many more includes

namespace Core {

   class OrbitCacheManager
   {
   public:
        static std::pair<float,float> getValue(const std::pair<int,int>& type, float phase, float param)
        {
            auto cache = getCacheData(type);
            // interpolate values based on phase and param
            return calculated_value;
        }
   private:
        static std::vector<std::pair<float,float>>& getCacheData(const std::pair<int,int>& type)
        {
            if (type.first == 0 && type.second == 0) return cache_0_0::values;
            if (type.first == 1 && type.second == 0) return cache_1_0::values;
            // etc
        }

The cache files look like this:

cache_0_0.h:

#ifndef cache_0_0_h
#define cache_0_0_h 
#include <vector>
namespace Core {
class cache_0_0{
public:
    static std::vector<std::pair<float,float>> values;
};
};
#endif

cache_0_0.cpp:

#include "cache_0_0.h"
using namespace Core;
std::vector<std::pair<float,float>> cache_0_0::values = {
{ 0.000000, 1.000000 },     { 0.062791, 0.998027 }, // etc

This was ment to be run like this:

for (some phase range) {
    auto v = OrbitCacheManager::getValue(type, phase, param);
    // do something with v
}

This approach turn out really slow, the profiler showed lots of CPU peaks and the UI was really laggy.

When I refactored the getCacheData method in OrbitCacheManager.h to this:

static std::vector<std::pair<float,float>>* getCacheData(const std::pair<int,int>& type) 
{
    if (type.first == 0 && type.second == 0) return &(cache_0_0::values);

Everything started working as expected.

My question is, why did that change increased speed so dramatically?

I'am using clang c++11 on IOS

Mcafee answered 4/12, 2018 at 15:16 Comment(2)
You should state what the compiler options you used to compile your code. If you are running unoptimized code, the timings you're showing are meaningless.Yeargain
There was similar question of using auto vs auto & in a for range loop, maybe not a dup but could be relatedZerelda
C
7

You may be returning it by reference, but you are storing in another object, hence still doing a costly copy:

 auto& cache = getCacheData(type);

You should add the & everywhere you return by reference and expect to keep a reference and not a copy.

Cervix answered 4/12, 2018 at 15:18 Comment(7)
@NathanOliver added a note for the reference and fixed the answer.Cervix
Yes, it was that! I thought that auto already had the reference.Avidin
auto may capture the const, but it doesn't capture the &.Cervix
@MatthieuBrucher auto without a reference doesn't capture const either. It uses exactly the same rules as template argument decuction.Trig
@Angew const std::vector<int>& get() can be captured by auto&, you don't need the const, so it is captured in this case.Cervix
@MatthieuBrucher Yes, auto & can become const T &, but plain auto will never become a const T. I found the comment somewhat misleading.Trig
@Angew Agreed, that's definitely true.Cervix

© 2022 - 2024 — McMap. All rights reserved.