ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

c – GCC和Clang代码性能的巨大差异

2019-08-23 11:07:34  阅读:277  来源: 互联网

标签:c compiler-errors g clang-2


社区.我有这段代码在欧几里德3D空间中找到最近的一对点.这个问题既不是关于算法也不是它的实现或其他什么.问题是,使用GCC而不是Clang编译时运行速度要慢得多.最令人困惑的是,它在随机样本上具有可比较的执行时间,并且在某些特定样本上具有相似的100倍.
我怀疑GCC中可能存在一个错误,因为我无法想到任何其他选项.

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <ctime>
#include <fstream>
#include <cassert>
#include <complex>
#include <string>
#include <cstring>
#include <chrono>
#include <random>
#include <queue>

static std::mt19937 mmtw(std::chrono::steady_clock::now().time_since_epoch().count());
int64_t rng(int64_t x, int64_t y) {
  static std::uniform_int_distribution<int64_t> d;
  return d(mmtw) % (y - x + 1) + x;
}

constexpr static int MAXN = 1e5 + 10;

void solve(std::istream &in, std::ostream &out);

void generate(std::ostream &out) {
  constexpr int N = 1e5;
  out << N << '\n';
  int MIN = -1e6;
  int MAX = 1e6;
  for (int i = 0; i < N; ++i) {
    out << 0 << ' ';
    out << i << ' ';
    out << (i + 1) * int(1e4) << '\n';
  }
}

int main() {

  freopen("input.txt", "r", stdin);

  std::ios_base::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cout.tie(nullptr);
  std::cerr.tie(nullptr);

  std::ofstream fout("input.txt");
  generate(fout);
  fout.close();

  solve(std::cin, std::cout);

  return 0;
}

struct point_t {
  int32_t x, y, z;
  int id;
  point_t() = default;
  point_t(int32_t x, int32_t y, int32_t z) : x(x), y(y), z(z) {}
  point_t operator +(const point_t &rhs) const {
    return point_t(x + rhs.x, y + rhs.y, z + rhs.z);
  }
  point_t operator -(const point_t &rhs) const {
    return point_t(x - rhs.x, y - rhs.y, z - rhs.z);
  }
  int64_t abs2() const {
    return 1LL * x * x + 1LL * y * y + 1LL * z * z;
  }
};

std::istream &operator >>(std::istream &in, point_t &pt) {
  return in >> pt.x >> pt.y >> pt.z;
}

inline bool cmp_x(const point_t &lhs, const point_t &rhs) {
  return lhs.x < rhs.x;
}

inline bool cmp_y(const point_t &lhs, const point_t &rhs) {
  return lhs.y < rhs.y;
}

inline bool cmp_z(const point_t &lhs, const point_t &rhs) {
  return lhs.z < rhs.z;
}

struct pair_t {
  int64_t distance_sq;
  point_t a {}, b {};
  pair_t() : distance_sq(std::numeric_limits<int64_t>::max()) {};
  pair_t(const point_t &a, const point_t &b) : distance_sq((a - b).abs2()), a(a), b(b) {}
  bool operator<(const pair_t &rhs) const {
    return distance_sq < rhs.distance_sq;
  }
};

template <typename T> inline T sqr(T arg) { return arg * arg; }

point_t pts[MAXN];

static pair_t ans = pair_t();

void recur_2D(point_t pts[], int size, int64_t threshold_sq) {
  if (size <= 3) {
    for (int i = 0; i < size; ++i) {
      for (int j = i + 1; j < size; ++j) {
        ans = std::min(ans, pair_t(pts[i], pts[j]));
      }
    }
    std::sort(pts, pts + size, cmp_y);
    return;
  }
  int mid = size / 2;
  int midx = pts[mid].x;
  recur_2D(pts, mid, threshold_sq);
  recur_2D(pts + mid, size - mid, threshold_sq);

  static point_t buffer[MAXN];
  std::merge(pts, pts + mid, pts + mid, pts + size, buffer, cmp_y);
  std::copy(buffer, buffer + size, pts);

  int buff_sz = 0;
  for (int i = 0; i < size; ++i) {
    if (sqr(pts[i].x - midx) >= threshold_sq) {
      continue;
    }
    int64_t x_sqr = sqr(pts[i].x - midx);
    for (int j = buff_sz - 1; j >= 0; --j) {
      if (sqr(pts[i].y - buffer[j].y) + x_sqr >= threshold_sq) {
        break;
      }
      ans = std::min(ans, pair_t(pts[i], buffer[j]));
    }
    buffer[buff_sz++] = pts[i];
  }
}

void recur_3D(point_t pts[], int size) {
  if (size <= 3) {
    for (int i = 0; i < size; ++i) {
      for (int j = i + 1; j < size; ++j) {
        ans = std::min(ans, pair_t(pts[i], pts[j]));
      }
    }
    std::sort(pts, pts + size, cmp_x);
    return;
  }

  int mid = size / 2;
  int midz = pts[mid].z;

  recur_3D(pts, mid);
  recur_3D(pts + mid, size - mid);

  static point_t buffer[MAXN];
  std::merge(pts, pts + mid, pts + mid, pts + size, buffer, cmp_x);
  std::copy(buffer, buffer + size, pts);

  int buff_sz = 0;
  for (int i = 0; i < size; ++i) {
    if (sqr(pts[i].z - midz) >= ans.distance_sq) {
      continue;
    }
    buffer[buff_sz++] = pts[i];
  }
  recur_2D(buffer, buff_sz, ans.distance_sq);
}

void solve(std::istream &in, std::ostream &out) {

  clock_t start = clock();

  int num_of_points;
  in >> num_of_points;

  for (int i = 0; i < num_of_points; ++i) {
    in >> pts[i];
    pts[i].id = i + 1;
  }

  std::sort(pts, pts + num_of_points, cmp_z);
  recur_3D(pts, num_of_points);
  out << ans.distance_sq << '\n';
  out << 1.0 * (clock() - start) / CLOCKS_PER_SEC << " s.\n";
}

链接到此代码:https://code.re/2yfPzjkD

它生成的样本使代码非常慢,然后测量算法执行时间.

我编译

g -DLOCAL -std = c 1z -O3 -Wno-everything main.cpp

clang -DLOCAL -std = c 1z -O3 -Wno-everything main.cpp

并运行
./main,同时在同一目录中有input.txt.

Clang-copiled二进制文件的运行时间为0.053798秒.而海湾合作委员会在12.4276秒. .这些数字来自程序的输出,你可以看到该功能解决了.

我还在不同的编译器版本上验证了https://wandbox.org/的差异.
https://wandbox.org/permlink/YFEEWSKyos2dQf32 – 铿锵
https://wandbox.org/permlink/XctarNHvd3I1B0x8 – gcc

注意,我压缩输入,因此必须在解决时改变读数.

在我的本地机器上,我有这些编译器.

clang –version
clang版本7.0.0(标签/ RELEASE_700 / final)

g – 版本
g(GCC)8.2.1 20180831

感觉就像我在没有编译器优化的情况下运行GCC代码.可能是什么原因?

UPD.

此外,还有一个版本在一开始只调用std :: sort一次.
https://wandbox.org/permlink/i9Kd3GdewxSRwXsM

我也尝试用-stdlib = libstdc编译Clang,改组数据并认为std :: sort的不同实现不是原因.

解决方法:

这只是未定义的行为.由于有符号整数溢出,您的代码具有未定义的行为:

template <typename T> inline T sqr(T arg) { return arg * arg; }

您可以将其替换为:

template <typename T>
inline T sqr(T arg)
{
    assert(double(arg)*arg <= std::numeric_limits<T>::max());
    assert(double(arg)*arg >= std::numeric_limits<T>::min());
    return arg * arg;
}

并在调试器中捕获错误.它失败了,在行上从recur_3D调用了arg = -60000:

if (sqr(pts[i].z - midz) >= ans.distance_sq) {

这种情况发生在pts [i] = {x = 0,y = 0,z = 10000,id = 1}且midz = 70000.

因为这是不公平的行为,所有赌注都是关闭的.不同的编译以不同的方式利用“未定义的行为永远不会发生”的假设.这就是为什么clang和gcc的表现不同,而且它是纯粹的“运气”.

考虑使用UndefinedBehaviorSanitizer来捕获这些错误.我的clang安装没有它,但clang -fsanitize = signed-integer-overflow应该可以解决问题.

修复此功能可为clang和gcc提供相当的速度.

标签:c,compiler-errors,g,clang-2
来源: https://codeday.me/bug/20190823/1697186.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有