Efficient assignment of a STL vector to another STL vector (WSL Issue)

Viewed 191

I have an issue regarding the time in which the assignment of STL vector is done.

The context is: I'm reading a binary file into a std::vector like this:

std::vector<float> read_file(const std::string &file_path) {

    std::ifstream stream(file_path);

    if (!stream.good()) {
        std::cout << "Cannot open file located at: " << file_path << std::endl;
        return std::vector<float>();
    }

    stream.seekg(0, std::ios_base::end);    
    auto size = stream.tellg();
    stream.seekg(0, std::ios_base::beg);

    std::vector<float> values(size / sizeof(float));
    stream.read((char*) &values[0], size);

    stream.close();

    return values;
}

I have 128 binary files and each one contains ~2.500.000 float values.

Eventually I will have 128 x std::vector<float> vectors. However I want them stored in a list/vector (matrix should say) which turns into this data structure: std::vector<std::vector<float>>.

The problem is:

Example 1: The execution time of this code snippet will take ~700ms:

std::vector<float> data;
for (int i = 1; i <= 128; ++i) {
    data = read_file(getFile(i));
}

Example 2: But, the execution time of this code snippet will take ~2000ms:

std::vector<std::vector<float>> data(128);
for (auto i = 1; i <= 128; ++i) {
    data[i-1] = read_file(getFile(i));
}

From my understanding, the assignment will perform a moving operation if the right side is a vector&& and a copying operation if the right side is const vector&. Taking into account the RVO it's worthless to add a std::move to the return type, hence the value returned will not be copied but moved. However the assignment in both examples should do the same thing: assign the address of the returned vector (the right side) to the vector on the left side.

Question: Based on my understanding (which may be wrong) and taking into account the both examples, why there is such a big difference in execution time between Example1 and Example2 if both perform the same operation (the optimizations were activated). Is there any improvement I can make to reduce the time of the second example? (I want to make the second example as efficient as possible)

Thanks.

2 Answers

This is not an answer, it is a way to try to understand the difference of time, but this is too long to be given as a remark

In the first case you always reuse the same blocks in memory, the needed size during the execution is small

But in the second solution you need 128 times more memory, may be that explain the difference of time ? and this is enforced if the execution time is the real time (swap during execution ?)

To compare the cases :

int main(int, char ** argv)
{
  switch (*argv[1]) {
  case '1':
    {
      // this is your first case
      std::vector<float> data;
      for (int i = 1; i <= 128; ++i) {
        data = read_file(getFile(i));
      }
    }
    break;
  case '2':
    {
      // this is your seconde case
      std::vector<std::vector<float>> data(128);
      for (auto i = 1; i <= 128; ++i) {
        data[i-1] = read_file(getFile(i));
      }
    }
    break;
  default:
    {
      // this is equivalent to your first case EXCEPT that needs 128 times more memory 
      std::vector<std::vector<float> *> data(128);
      for (auto i = 1; i <= 128; ++i) {
        data[i-1] = new std::vector<float>();
        *(data[i-1]) = read_file(getFile(i));
      }
    }
    break;
  }

  return 0;
}

When you run that with argument 3 do you get the time when the argument is 1 or 2 ? If 2 and 3 are similar that means the reason is the higher memory size needed.

The problem I identified is the WSL environment (through which I'm compiling, using CLion) that has a low I/O performance. Even though I didn't mention this aspect in my question, I had to compile the same program on various platforms and systems in order to understand what exactly is happening.

Setting up another building environment fixed the issue (the execution time is now very similar for Example 1 and 2 as well)

Related