vector versus pointer access

For the following code:
1
2
3
4
5
6
7
8
9
10
11
for (int i = 0; i < 4000; i++)
{
				
for (int j = 0; j < 5000; j++)
{
table.at<double>(i, j) = 99;	
}

std::cout << endl;
			
}

I believe I am using the vector container approach? The only matrix operations used are looking up values - nothing more. In this example I just printout the values and set them. But in the greater picture would there be enough of a speed improvement to warrant a change from this to pointer access instead? My program uses machine vision.
Last edited on
All we can tell from the code you've posted is that you're using some kind of class that has a method with signature at(size_type, size_type). That can't be a normal std::vector, because std::vector::at() only takes a single size_type argument.

Without seeing the definition of table, and the definition of its type, we can't know what's actually being used under the hood. It certainly looks like the interface to that class has been designed to use similar semantics to a vector, albeit adapted to a 2-dimensional table.

I'm impressed that you've been here for over a decade, and still haven't learnt how to write a good, clear question that provides all the relevant information that might help us give you the answers you're looking for.
Last edited on
just use [] which is supported by vector and you will see a large speedup. .at does a safety check which is very slow (in comparison to []) but [] can go out of bounds without alerting you, so be sure you get your indices correct.
that just becomes table[r][c] assuming table is a 2-d vector of double (?)

two D vectors can have memory access inefficiency. each internal vector is only one row, and each row can be anywhere in ram. If you want to fix that, you would need to rewrite how you define the container, make it a 1-d vector and apply an accessor to make it seem as if 2-d (the formula is table[r][c] is r*numberofcolumns+c)

using a raw pointer would not much faster than using a 1-d vector. The thin layer added by a vector is not costing you anything; the performance penalties are all from that .at and the memory layout. You *can* reshape a 1d raw pointer into 2d and do [][] access if you want to go there I can show you the screwball syntax to do that.

-------------
edit, good catch... its not a vector. I didn't look closely enough.
in that case, if its just a blasted lookup table, rewrite it to be something simple and efficient.
Last edited on
Here is the mat creation & attributes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int size[3] = { 500, 800, 2 }; // row major
	cv::Mat table(3, size, CV_32FC1, cv::Scalar(0)); //64FC1
	//write to mat

	std::cout << "create the mat" << std::endl;
	
	for (int i = 0; i < 500; i++)
	{
		for (int j = 0; j < 800; j++)
		{
			for (int k = 0; k < 1; k++)
				
			{
				float random_1 = rand() % 100;
				float random_2 = rand() % 100;

				
				table.at<cv::Vec3f>(i, j)[0] = random_1;
			        table.at<cv::Vec3f>(i, j)[1] = random_2;
				
			}
		}
	}


Mat Access:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (int i = 0; i < 500; i++)
	{
		for (int j = 0; j < 800; j++)
		{
			
			for (int k = 0; k < 1; k++)
				
			{
				
				std::cout << "image x,y: (" << table.at<cv::Vec3f>(i, j)[k] << "," << 
                                table.at<cv::Vec3f>(i, j)[k+1] << ")" << std::endl;
			
			}
		}
	}


I hope that's enough.
Last edited on
Using at() indexing with a 2D vector (SSCCE code - http://www.sscce.org/):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>

int main()
{
   std::vector<std::vector<double>> table(4000, std::vector<double>(5000));

   for (int i = 0; i < 4000; i++)
   {
      for (int j = 0; j < 5000; j++)
      {
         table.at(i).at(j) = 99;
      }
   }

   std::cout << "Done! " << table.at(0).at(0) << '\n';
}

As jonnin points out at() is damned slow because of the built-in checks for out-of-boundary checks it does.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>

int main()
{
   std::vector<std::vector<double>> table(4000, std::vector<double>(5000));

   for (int i = 0; i < 4000; i++)
   {
      for (int j = 0; j < 5000; j++)
      {
         table[i][j] = 99;
      }
   }

   std::cout << "Done! " << table[0][0] << '\n';
}

Even with operator[] there is a noticeable bit of lag before the output because you are doing 20 million loops. I am not motivated enough to time the difference in indexing.

BTW, should you use at() and you go out of bounds the program will crash and burn without exception handling.
KISS it:

1
2
3
4
5
6
7
8
9
10
11
12
13

int main()
{

  double * table1d = new double[4000*5000];
  typedef double col[5000];
  for(int i = 0; i < 4000*5000; i++)
	  table1d[i] = i;
  
  col* table = (col*)table1d;
	cout << table[3][10];
	
}

if you ever need linear access use table1d. If you need 2d access use table.
that should be a unique pointer if you can get it to work with all the smoke and mirrors. If not, delete table when done, etc.
Note: This BS only works if you know the dimensions. If they are at run-time, there is no way to do it that I know of, but I have not tried that very hard either.
Note: table1d can be a const sized vector, but if it changes size the pointer will invalidate.
Last edited on
If I KISS it then I need code that uses a pointer for 3d array access. I don't know whether
that code can do that already.

Every i,j index should store an additional [0] and [1]:
1
2
3
4

table.at<cv::Vec3f>(i, j)[0] = random_1;
table.at<cv::Vec3f>(i, j)[1] = random_2;
		


BTW, should you use at() and you go out of bounds the program will crash and burn without exception handling.


Noted - thx
Last edited on
No, I did NOT state that operator[] itself is "a time conusmer [sic]."

Looping through 20 million (4'000 by 5'000) elements, or more, is gonna be laggy no matter how the data is structured or accessed. 1D, 2D or 3D.

Using at() will take longer still, maybe not really noticeable without actually timing the differences, there are internal bounds checking going on vs. operator[].

Dealing with huge gobs of data won't be quick, period.
the # of dimensions is irrelevant, though past 3 is a bit hard to keep track of.

It works the exact same way.
1
2
3
4
5
6
7
8
9
10
11
12
int main()
{

  double * table1d = new double[4000*5000*2];
  typedef double col[5000][2];
  for(int i = 0; i < 4000*5000*2; i++)
	  table1d[i] = i;
  
  col* table = (col*)table1d;
	cout << table[3][10][1];
	
}


any iteration over so much should be threaded.
Last edited on

My apologies.
Last edited on
Is it possible to write a matrix with a 3d vector, then access it via pointers?
not fully. vector of vectors isn't a solid block of memory**. You can address each hop with a pointer, but [] is already doing that at each level, and you won't gain anything at all.
but a 1d vector can be reshaped with an accessor pointer same as I did above. However if you do that, you have to redo the pointer if the vector reallocates itself.

don't confuse the performance things (.at and memory layout) with the syntax sugar (all this crap with 1-d is doing the same thing: providing, in various ways, a syntax into 1-d that looks like 2d or 3d etc. The reason for 1d is just to get that solid block memory layout.).

**if you are forcing the memory layout with some sort of manager, it could work if you are really spot on with it. I am not sure how to do that, as I have not needed to do so.
Last edited on
Please explain some about the bigger picture.

You can't effectively read a map by looking at it through a microscope. Similarly, information about microscopic details won't help you find your way to a working product.
What is the problem your final product intends to solve?
How does this matrix relate to that problem?

Most implementations of std::vector<T> have essentially this structure or some variation of it:
1
2
3
4
5
6
7
8
struct my_vector 
{ 
  [[no_unique_address]] 
    my_allocator<T> alloc;
  T* begin_elements; 
  T* end_elements; 
  T* end_of_storage; 
};

That is, every access to a vector's elements is done indirectly through one of the pointers stored inside that vector.

To access an int that is "stored inside" a vector of vector of int, two indirections are normally required. One to locate the nth vector within the outer vector of vectors; then another to locate the mth int in the nth vector.
Last edited on
Topic archived. No new replies allowed.