为什么我在C++中的代码比在C中的代码慢得多

Why is my code in C++ so much slower than my code in C

本文关键字:代码 为什么 C++      更新时间:2023-10-16

到目前为止,我用C编写了代码(性能至关重要)。然而,我想开始以一种通用的方式编写我的算法。所以,我决定试用C++。我用C语言编写了一个简单的代码,并用模板将其翻译成C++。令我失望的是,C++代码的运行速度慢了2.5倍。(C代码用gcc-O3编译;C++代码用g++-O3编译)

我在C++中做错了什么吗?为什么会有这样的表演?

这是C代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static int df_output = 0;
int nCalls = 0;
typedef struct {
  int *pancakes;
  int n;
} STATE;
STATE **solution;
void shuffle(STATE *s) {
  int i;
  for (i = 0; i < s->n; i++) {
    int i1 = rand() % s->n;
    int i2 = rand() % s->n;
    int temp = s->pancakes[i1];
    s->pancakes[i1] = s->pancakes[i2];
    s->pancakes[i2] = temp;
  }
}
STATE *copyState(STATE *s) {
  STATE *res = malloc(sizeof(STATE));
  res->n = s->n;
  res->pancakes = (int *)malloc(res->n * sizeof(int));
  memcpy(res->pancakes, s->pancakes, res->n * sizeof(int));
  return res;
}
// reverse n pancakes
void makeMove(STATE *s, int n) {
  int i;
  for (i = 0; i < n/2; i++) {
    int temp = s->pancakes[i];
    s->pancakes[i] = s->pancakes[n - 1 - i];
    s->pancakes[n - 1 - i]=temp;
  }
}
void printState(STATE *s) {
  int i;
  printf("[");
  for (i = 0; i < s->n; i++) {
    printf("%d", s->pancakes[i]);
    if (i < s->n - 1)
      printf(", ");
  }
  printf("]");
}
int heuristic(STATE *s) {
  int i, res = 0;
  nCalls++;
  for (i = 1; i < s->n; i++)
    if (abs(s->pancakes[i]-s->pancakes[i-1])>1)
      res++;
  if (s->pancakes[0] != 0) res++;
  return res;
}
void tabs(int g) {
  int i;
  for (i = 0; i < g; i++) printf("t");
}
int df(STATE *s, int g, int left) {
  int h = heuristic(s), i;
  if (g == 0) printf("Thereshold: %dn", left);
  if (df_output) {
    tabs(g);
    printf("g=%d,left=%d ", g, left); printState(s); printf("n");}
  if (h == 0) {
    assert(left == 0);
    solution = (STATE **)malloc((g+1) * sizeof(STATE *));
    solution[g] = copyState(s);
    return 1;
  }
  if (left == 0)
    return 0;
  for (i = 2; i <= s->n; i++) {
    makeMove(s, i);
    if (df(s, g+1, left-1)) {
      makeMove(s, i);
      solution[g] = copyState(s);
      return 1;
    }
    makeMove(s, i);
  }
  return 0;
}
void ida(STATE *s) {
  int threshold = 0, i;
  while (!df(s, 0, threshold)) threshold++; 
  for (i = 0; i <= threshold; i++) {
    printf("%d. ", i);
    printState(solution[i]);
    printf("n");
    //if (i < threshold - 1) printf("->");
  }
}
int main(int argc, char **argv) {
  STATE *s = (STATE *)malloc(sizeof(STATE));
  int i, n;
  int myInstance[] = {0,5,4,7,2,6,1,3};
  s->n = 8;
  s->pancakes = myInstance;
  printState(s); printf("n");
  ida(s);
  printf("%d calls to heuristic()", nCalls);
  return 0;
}

这是C++代码:

#include <iostream>
#include "stdlib.h"
#include "string.h"
#include "assert.h"
using namespace std;
static int df_output = 0;
int nCalls = 0;
class PancakeState {
public:
  int *pancakes;
  int n;
  PancakeState *copyState();
  void printState();
};
PancakeState *PancakeState::copyState() {
  PancakeState *res = new PancakeState();
  res->n = this->n;
  res->pancakes = (int *)malloc(this->n * sizeof(int));
  memcpy(res->pancakes, this->pancakes, 
     this->n * sizeof(int));
  return res;
}
void PancakeState::printState() {
  int i;
  cout << "[";
  for (i = 0; i < this->n; i++) {
    cout << this->pancakes[i];
    if (i < this->n - 1)
      cout << ", ";
  }
  cout << "]";
}
class PancakeMove {
public:
  PancakeMove(int n) {this->n = n;}
  int n;
};
class Pancake {
public:
  int heuristic (PancakeState &);
  int bf(PancakeState &);
  PancakeMove *getMove(int);
  void makeMove(PancakeState &, PancakeMove &);
  void unmakeMove(PancakeState &, PancakeMove &);
};
int Pancake::bf(PancakeState &s) {
  return s.n - 1;
}
PancakeMove *Pancake::getMove(int i) {
  return new PancakeMove(i + 2); 
}
// reverse n pancakes
void Pancake::makeMove(PancakeState &s, PancakeMove &m) {
  int i;
  int n = m.n;
  for (i = 0; i < n/2; i++) {
    int temp = s.pancakes[i];
    s.pancakes[i] = s.pancakes[n - 1 - i];
    s.pancakes[n - 1 - i]=temp;
  }
}
void Pancake::unmakeMove(PancakeState &state, PancakeMove &move) {
  makeMove(state, move);
}
int Pancake::heuristic(PancakeState &s) {
  int i, res = 0;
  nCalls++;
  for (i = 1; i < s.n; i++)
    if (abs(s.pancakes[i]-s.pancakes[i-1])>1)
      res++;
  if (s.pancakes[0] != 0) res++;
  return res;
}
void tabs(int g) {
  int i;
  for (i = 0; i < g; i++) cout << "t";
}
template <class Domain, class State, class Move>
class Alg {
public:
  State **solution;
  int threshold;
  bool verbose;
  int df(Domain &d, State &s, int g);
  void ida(Domain &d, State &s);
};
template <class Domain, class State, class Move>
int Alg<Domain, State, Move>::df(Domain &d, State &s, int g) {
  int h = d.heuristic(s), i;
  if (g == 0) 
    cout << "Thereshold:" << this->threshold << "n";
  if (this->verbose) {
    tabs(g);
    cout << "g=" << g;
    s.printState(); cout << "n";
  }
  if (h == 0) {
    solution = (State **)malloc((g+1) * sizeof(State *));
    solution[g] = s.copyState();
    return 1;
  }
  if (g == this->threshold)
    return 0;
  for (i = 0; i < d.bf(s); i++) {
    Move *move = d.getMove(i);
    d.makeMove(s, *move);
    if (this->df(d, s, g+1)) {
      d.unmakeMove(s, *move);
      solution[g] = s.copyState();
      delete move;
      return 1;
    }
    d.unmakeMove(s, *move);
    delete move;
  }
  return 0;
}
template <class Domain, class State, class Move>
void Alg<Domain, State, Move>::ida(Domain &d, State &s) {
  int i;
  this->threshold = 0;
  while (!this->df(d, s, 0)) threshold++; 
  for (i = 0; i <= threshold; i++) {
    cout << i << ".";
    this->solution[i]->printState();
    cout << "n";
    //if (i < threshold - 1) printf("->");
  }
}
int main(int argc, char **argv) {
  Pancake *d = new Pancake();
  PancakeState *s = new PancakeState();
  int myInstance[] = {0,5,4,7,2,6,1,3};
  s->pancakes = myInstance;
  s->n = 8;
  s->printState(); cout << "n";
  Alg<Pancake, PancakeState, PancakeMove> *alg = new Alg<Pancake, PancakeState, PancakeMove>();
  //alg->verbose = true;
  alg->ida(*d, *s);
  cout << nCalls < "calls to heuristic()";
  delete alg;
  return 0;
}

您有很多malloc()和操作符的新调用。停止这样做,性能就会提高。不要在C++中使用malloc(),始终使用运算符new。

例如,PancakeMove是一个小而琐碎的结构。但是您可以动态地分配它的实例,这很慢。只需按价值传递即可。

基本上,您在堆上而不是在堆栈上分配很多小东西。这相当"昂贵",所以需要额外的时间。

此代码(从原始代码修改而来)在C代码的1ms内运行:

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
using namespace std;
static int df_output = 0;
int nCalls = 0;
class PancakeState {
public:
    PancakeState(int n) : n(n), pancakes(n) 
    {
    }
    PancakeState(int n, int *v) : n(n), pancakes(n) 
    {
        for(int i = 0; i < n; i++)
        pancakes[i] = v[i];
    }
    PancakeState(): n(0) {}
public:
    vector<int> pancakes;
    int n;
    PancakeState *copyState();
    void printState();
};
void PancakeState::printState() {
  int i;
  cout << "[";
  for (i = 0; i < this->n; i++) {
    cout << this->pancakes[i];
    if (i < this->n - 1)
      cout << ", ";
  }
  cout << "]";
}
class PancakeMove {
public:
    PancakeMove(int n) : n(n)  {}
    int n;
};
class Pancake {
public:
  int heuristic (PancakeState &);
  int bf(PancakeState&);
  PancakeMove getMove(int);
  void makeMove(PancakeState &, PancakeMove &);
  void unmakeMove(PancakeState &, PancakeMove &);
};
int Pancake::bf(PancakeState& s) {
  return s.n - 1;
}
PancakeMove Pancake::getMove(int i) {
  return PancakeMove(i + 2); 
}
// reverse n pancakes
void Pancake::makeMove(PancakeState &s, PancakeMove &m) {
  int i;
  int n = m.n;
  for (i = 0; i < n/2; i++) {
    int temp = s.pancakes[i];
    s.pancakes[i] = s.pancakes[n - 1 - i];
    s.pancakes[n - 1 - i]=temp;
  }
}
void Pancake::unmakeMove(PancakeState &state, PancakeMove &move) {
  makeMove(state, move);
}
int Pancake::heuristic(PancakeState &s) {
  int i, res = 0;
  nCalls++;
  for (i = 1; i < s.n; i++)
    if (abs(s.pancakes[i]-s.pancakes[i-1])>1)
      res++;
  if (s.pancakes[0] != 0) res++;
  return res;
}
void tabs(int g) {
  int i;
  for (i = 0; i < g; i++) cout << "t";
}
template <class Domain, class State, class Move>
class Alg {
public:
  vector<State> solution;
  int threshold;
  bool verbose;
  int df(Domain &d, State &s, int g);
  void ida(Domain &d, State &s);
};
template <class Domain, class State, class Move>
int Alg<Domain, State, Move>::df(Domain &d, State &s, int g) {
  int h = d.heuristic(s), i;
  if (g == 0) 
    cout << "Thereshold:" << threshold << "n";
  if (this->verbose) {
    tabs(g);
    cout << "g=" << g;
    s.printState(); cout << "n";
  }
  if (h == 0) {
      solution.resize(g+1);
      solution[g] = s;
      return 1;
  }
  if (g == this->threshold)
    return 0;
  for (i = 0; i < d.bf(s); i++) {
    Move move = d.getMove(i);
    d.makeMove(s, move);
    if (this->df(d, s, g+1)) {
      d.unmakeMove(s, move);
      solution[g] = s;
      return 1;
    }
    d.unmakeMove(s, move);
  }
  return 0;
}
template <class Domain, class State, class Move>
void Alg<Domain, State, Move>::ida(Domain &d, State &s) {
  int i;
  this->threshold = 0;
  while (!this->df(d, s, 0)) threshold++; 
  for (i = 0; i <= threshold; i++) {
    cout << i << ".";
    solution[i].printState();
    cout << "n";
    //if (i < threshold - 1) printf("->");
  }
}
int main(int argc, char **argv) {
  Pancake d = Pancake();
  int myInstance[] = {0,5,4,7,2,6,1,3};
  PancakeState s(8, myInstance);
  s.printState(); cout << "n";
  Alg<Pancake, PancakeState, PancakeMove> *alg = new Alg<Pancake, PancakeState, PancakeMove>();
  //alg->verbose = true;
  alg->ida(d, s);
  cout << nCalls < "calls to heuristic()";
  delete alg;
  return 0;
}

作为不进行这么多直接分配的额外好处,它在整个执行过程中也不会泄漏22块内存,这是一个非常有用的功能。

(如果你想看看发生了什么变化,这里有一个差异-只忽略空白的变化):

--- pcake.orig.cpp  2014-04-13 15:43:24.861417827 +0100
+++ pcake.cpp   2014-04-13 15:42:25.145165372 +0100
@@ -1,7 +1,9 @@
 #include <iostream>
-#include "stdlib.h"
-#include "string.h"
-#include "assert.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <vector>
+
 using namespace std;
 static int df_output = 0;
@@ -9,21 +11,22 @@
 class PancakeState {
 public:
-  int *pancakes;
+    PancakeState(int n) : n(n), pancakes(n) 
+   {
+   }
+    PancakeState(int n, int *v) : n(n), pancakes(n) 
+   {
+       for(int i = 0; i < n; i++)
+       pancakes[i] = v[i];
+   }
+    PancakeState(): n(0) {}
+public:
+    vector<int> pancakes;
   int n;
   PancakeState *copyState();
   void printState();
 };
-PancakeState *PancakeState::copyState() {
-  PancakeState *res = new PancakeState();
-  res->n = this->n;
-  res->pancakes = (int *)malloc(this->n * sizeof(int));
-  memcpy(res->pancakes, this->pancakes, 
-     this->n * sizeof(int));
-  return res;
-}
-
 void PancakeState::printState() {
   int i;
   cout << "[";
@@ -37,25 +40,25 @@
 class PancakeMove {
 public:
-  PancakeMove(int n) {this->n = n;}
+    PancakeMove(int n) : n(n)  {}
   int n;
 };
 class Pancake {
 public:
   int heuristic (PancakeState &);
-  int bf(PancakeState &);
-  PancakeMove *getMove(int);
+  int bf(PancakeState&);
+  PancakeMove getMove(int);
   void makeMove(PancakeState &, PancakeMove &);
   void unmakeMove(PancakeState &, PancakeMove &);
 };
-int Pancake::bf(PancakeState &s) {
+int Pancake::bf(PancakeState& s) {
   return s.n - 1;
 }
-PancakeMove *Pancake::getMove(int i) {
-  return new PancakeMove(i + 2); 
+PancakeMove Pancake::getMove(int i) {
+  return PancakeMove(i + 2); 
 }
 // reverse n pancakes
@@ -91,7 +94,7 @@
 template <class Domain, class State, class Move>
 class Alg {
 public:
-  State **solution;
+  vector<State> solution;
   int threshold;
   bool verbose;
   int df(Domain &d, State &s, int g);
@@ -102,30 +105,28 @@
 int Alg<Domain, State, Move>::df(Domain &d, State &s, int g) {
   int h = d.heuristic(s), i;
   if (g == 0) 
-    cout << "Thereshold:" << this->threshold << "n";
+    cout << "Thereshold:" << threshold << "n";
   if (this->verbose) {
     tabs(g);
     cout << "g=" << g;
     s.printState(); cout << "n";
   }
   if (h == 0) {
-    solution = (State **)malloc((g+1) * sizeof(State *));
-    solution[g] = s.copyState();
+      solution.resize(g+1);
+      solution[g] = s;
     return 1;
   }
   if (g == this->threshold)
     return 0;
   for (i = 0; i < d.bf(s); i++) {
-    Move *move = d.getMove(i);
-    d.makeMove(s, *move);
+    Move move = d.getMove(i);
+    d.makeMove(s, move);
     if (this->df(d, s, g+1)) {
-      d.unmakeMove(s, *move);
-      solution[g] = s.copyState();
-      delete move;
+      d.unmakeMove(s, move);
+      solution[g] = s;
       return 1;
     }
-    d.unmakeMove(s, *move);
-    delete move;
+    d.unmakeMove(s, move);
   }
   return 0;
 }
@@ -138,23 +139,22 @@
   for (i = 0; i <= threshold; i++) {
     cout << i << ".";
-    this->solution[i]->printState();
+    solution[i].printState();
     cout << "n";
     //if (i < threshold - 1) printf("->");
   }
 }
 int main(int argc, char **argv) {
-  Pancake *d = new Pancake();
-  PancakeState *s = new PancakeState();
+  Pancake d = Pancake();
   int myInstance[] = {0,5,4,7,2,6,1,3};
-  s->pancakes = myInstance;
-  s->n = 8;
-  s->printState(); cout << "n";
+  PancakeState s(8, myInstance);
+  s.printState(); cout << "n";
   Alg<Pancake, PancakeState, PancakeMove> *alg = new Alg<Pancake, PancakeState, PancakeMove>();
   //alg->verbose = true;
-  alg->ida(*d, *s);
+  alg->ida(d, s);
   cout << nCalls < "calls to heuristic()";
   delete alg;
   return 0;
 }
+