Performance of operator overloading vs function ca

2019-07-25 07:41发布

问题:

Let us suppose I create my own vector class as follows:

<template class T>
class Vector {
private:
  void** ptr;
  // More functions implementing the custom vector
public:
  T& operator[](int iIndex) const {
    return *(T*)ptr[iIndex];
  }
  T& Item(int iIndex) const {
    return *(T*)ptr[iIndex];
  }
}

Let say, I have a Vector<someClass> v. Strictly, performance-wise which of these is faster for accessing an element of the vector.

  1. v.Item(i)
  2. v[i]

回答1:

v[i] is merely syntactic sugar for v.operator[](i).

There will be no difference in performance, insofar that there is no difference in performance between two functions with different names that are identical in all other respects.



回答2:

To add to @Bathsheba's answer, I did a quick test with a slightly more complete dummy Vector implementation:

template <class T>
class Vector {
private:
  T* ptr;
public:
  Vector(int size)
    : ptr(new T[size])
  { }
  ~Vector()
  {
    delete[] ptr;
  }
  T& operator[](int iIndex) const {
    return ptr[iIndex];
  }
  T& Item(int iIndex) const {
    return ptr[iIndex];
  }
};

int main()
{
  Vector<int> v(5);
  v[0] = 3;
  v.Item(1) = 4;
}

Compiled with g++ -S, I get exactly identical assembly for both methods (look for _ZNK6VectorIiEixEi = Vector<int>::operator[](int) const and _ZNK6VectorIiE4ItemEi = Vector<int>::Item(int) const in the assembly output). This is expected, since both operator[]() and Item() are function calls. The operator notation is just syntactic sugar.



回答3:

As said in comments an in the other response, no difference at all.

operator will generate a call to operator, and since those two functions have the same code, they generate the same assembly, hence yielding the exact same performance.

For the sake of completeness, I tried with clang 3.7 on the following input code:

 template <typename T>
class Vector {
private:
  void** ptr;


public:

    Vector(void** _ptr): ptr(_ptr) {}

  T& operator[](const int iIndex) const {
    return *(T*)ptr[iIndex];
  }

  T& Item(const int iIndex) const {
    return *(T*)ptr[iIndex];
  }
};

extern "C" int printf(const char* format, ...);

int main() {
    double a[2] = { 1.0, 2.0 };
    Vector<double> va((void**) &a);

    double a1 = va[0];
    printf("%lf\n", a1);
    double a2 = va.Item(0);
    printf("%lf\n", a2);
}

compiled with:

clang -O3 -g -S -emit-llvm main.cpp -o main.ll

it effectively generates the exact same lines:

 ; Function Attrs: nounwind uwtable  
define i32 @main() #0 {  
entry:
  %a.sroa.0.0.copyload = load double*, double** bitcast ([2 x double]* @_ZZ4mainE1a to double**), align 16, !dbg !57
  %0 = load double, double* %a.sroa.0.0.copyload, align 8, !dbg !66, !tbaa !67
  %call1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), double %0), !dbg !72
  %1 = load double, double* %a.sroa.0.0.copyload, align 8, !dbg !76, !tbaa !67
  %call3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), double %1), !dbg !78
  ret i32 0, !dbg !79
}