0

I don't even know if the idea of argument forwarding is an actual topic, or if the term describes properly what I have in mind. I'll just start with an example.

class sort_of_vector
{
   // Some data here. Much data.
   size_t size;

   double property1(unsigned int index)
   {
      // Do some computation on the index and data.
   }

   double property2(unsigned int index)
   {
      // Do some computation on the index and data.
   }
};

Here, sort_of_vector is, indeed, a sort of vector, or looks like it is some sort of vector. It represents a collection of numbered elements, each of which has two (say double) properties. As you can see, those properties are however accessible only through getter methods, because some complicated computation needs to be carried out before returning the actual value.

Now, what I would like to do is to make sort_of_vector more similar, in terms of interfaces, to an actual vector. I would like a [] operator to be callable on that, so I could then call a getter for property1 and property2 on something returned via the [] operator.

There is a trivial way to do this:

class sort_of_vector
{
   // Same as before

   sort_of_vector_proxy operator [] (unsigned int index)
   {
      return sort_of_vector_proxy(this, index);
   }
};

class sort_of_vector_proxy
{
   sort_of_vector * vector;
   unsigned int index;

   sort_of_vector_proxy(sort_of_vector * vector, unsigned int index)
   {
      this->vector = vector;
      this->index = index;
   }

   double property1()
   {
      return this->vector->property1(this->index);
   }

   double property2()
   {
      return this->vector->property1(this->index);
   }
};

This obviously works, but it has some issues in terms of performance. In principle, accessing the property1 or property2 of some item would mean make a whole new sort_of_vector_proxy set its properties, call the actual getter through it and delete it.

So I was wondering if there were more performing solutions that can make my sort_of_vector more similar to a vector in this kind of fashion (my_sort_of_vector[33].property1()) but without having any performance downside.

Matteo Monti
  • 8,362
  • 19
  • 68
  • 114
  • I don't see any performance concerns here. Have you **measured** it? Creating the proxy only means creating 2 integer values (a pointer and an index), and there is one more indirection when calling the property. Moreover, I suspect the compiler will optimize this and non-convoluted calls with the proxy will be assembly near identical with the non-proxy version. – bolov May 23 '15 at 17:34
  • Thanks @bolov! In a real world context, I have the following problems: 1) the actual getter operation is **really, really** fast and completely crucial in terms of performance, so that adding even such a small amount of operation has a significant impact in terms of performance. 2) I would like to get everything inlined, and as far as I know constructors cannot be inlined. 3) This is a simple example to make everything clear, but I am working with quite convoluted template recursions so that I fear that in real world situations getter calls could not be so plain after all. – Matteo Monti May 23 '15 at 17:38
  • constructors can and if fact most of the time are inlined. That aside, i cannot emphasize enough how any talk abut performance without profiling is in vain. You should profile and compare and then if you see that the proxy is a real problem, you can think about optimizing it. Until you do measure, I strongly suspect it is a non-issue. – bolov May 23 '15 at 17:53
  • ...Think about this: Assuming that the compiler doesn't optimize this, probably the complicated computations take easily at least 1000 times more time than setting the proxy and the indirect call. What would you gain in the terms of the whole program if you would optimize the proxy to 0 overhead? Less than 0.1%. Is it worth it? What if you invested your time in optimizing the complicated computation to gain even as few as 10% improvement? **Profile**, identify the hot spots, and then optimize those hot spots specifically. **Profile** – bolov May 23 '15 at 17:53
  • I removed the term "complicated computations": I used it just to signify that it wasn't possible to just give external access to some list of members. In my case, indeed, often times those computations are not complicated at all (say, a sum and a dereferenciation), so that indeed the overhead of creating and destroying the proxy would be approximately the same order of magnitude of the getter, in terms of time, and I am not really willing to sacrifice half of my performance for the sake of a pretty call. However, indeed, I'll just have to see what happens at runtime. – Matteo Monti May 23 '15 at 18:07
  • I am still curious, though: is that possible that there is no way to do such a thing without affecting the output code? I mean, in principle it would all be a matter of substituting `[n].property1()` with `.property1(n)`. This really doesn't have to have an effect at runtime! – Matteo Monti May 23 '15 at 18:09
  • To give you an example of what I mean. I had a graphical application (so it had to ran at a certain FPS). I had a container of objects. At each frame from this container elements had to be deleted and inserted at various positions. Although lists have better complexity than vector at inserting/removing elements from random positions, I measured both and vector actually performed better (due to cache locality). First lesson: don't assume, measure.... – bolov May 23 '15 at 18:11
  • ...Then, because the application was performing poorly I spent some hours implementing a complex algorithm to reduce the number of inserts/deletes. And although on paper this should have been faster, it practice it performed the same. Why? Well I finally decided to properly profile my application and saw that more than 80% of the time the program spend just drawing. The management of objects was almost neglectable. So I undid my work and in 5 minutes spent on optimizing the drawing I managed to gain more than 2 times speedup on the whole program. – bolov May 23 '15 at 18:11
  • `This really doesn't have to have an effect at runtime! ` My point throughout this was that probably it doesn't. The compiler might optimize it, completely removing the proxy. – bolov May 23 '15 at 18:13
  • You couldn't be more clear. Thank you very much! – Matteo Monti May 23 '15 at 18:16
  • One good example here would be the iterators on c++. Practically they are proxies. Calling `*it` means calling a function. But looking at the assembly generated with `-O2` or `-O3` you can see that no function is called. No constructor for the iterator is called. All you see is a simple deference. – bolov May 23 '15 at 18:20

0 Answers0