小编Ins*_*oop的帖子

还有理由在C++代码中使用`int`吗?

许多样式指南(例如Google建议使用int索引数组时作为默认整数使用).随着64位平台的兴起,大多数时候a int只有32位,这不是平台的自然宽度.因此,除了简单的说法,我认为没有理由保持这种选择.我们清楚地看到编译以下代码的位置:

double get(const double* p, int k) {
  return p[k];
}
Run Code Online (Sandbox Code Playgroud)

被编译成

movslq %esi, %rsi
vmovsd (%rdi,%rsi,8), %xmm0
ret
Run Code Online (Sandbox Code Playgroud)

其中第一条指令将32位整数提升为64位整数.

如果代码转换成

double get(const double* p, std::ptrdiff_t k) {
  return p[k];
}
Run Code Online (Sandbox Code Playgroud)

生成的程序集现在

vmovsd (%rdi,%rsi,8), %xmm0
ret
Run Code Online (Sandbox Code Playgroud)

这清楚地表明,CPU感觉更在家std::ptrdiff_t比使用int.许多C++用户已经迁移到了std::size_t,但我不想使用无符号整数,除非我真的需要模数2^n行为.

在大多数情况下,使用int不会损害性能,因为未定义的行为或有符号整数溢出允许编译器在内部将任何内容int提升为std::ptrdiff_t处理索引的in循环,但我们从上面清楚地看到编译器不会感到宾至如归int.此外,std::ptrdiff_t在64位平台上使用会使溢出不太可能发生,因为当我们看到越来越多的人被int溢出困住时,他们必须处理大于2^31 - 1现在变得非常普遍的整数.

从我所看到的,这使得唯一int脱颖而出似乎是文字,如事实5int,但我不认为它会引起任何问题,如果我们移动到std::ptrdiff_t一个默认的整数.

我即将成为std::ptrdiff_t我小公司编写的所有代码的事实上的标准整数.这有什么理由可能是一个糟糕的选择吗?

PS:我同意这个名字 …

c++

181
推荐指数
7
解决办法
2万
查看次数

使用intel编译器在Windows和Linux之间的性能差异:查看程序集

我在Windows和Linux(x86-64)上运行程序.它使用相同的编译器(Intel Parallel Studio XE 2017)编译,具有相同的选项,Windows版本比Linux版本快3倍.罪魁祸首是对std :: erf的调用,在两种情况下都在英特尔数学库中解析(默认情况下,它在Windows上动态链接,在Linux上静态链接,但在Linux上使用动态链接可以提供相同的性能).

这是一个重现问题的简单程序.

#include <cmath>
#include <cstdio>

int main() {
  int n = 100000000;
  float sum = 1.0f;

  for (int k = 0; k < n; k++) {
    sum += std::erf(sum);
  }

  std::printf("%7.2f\n", sum);
}
Run Code Online (Sandbox Code Playgroud)

当我使用vTune分析这个程序时,我发现Windows和Linux版本之间的程序集有点不同.这是Windows上的调用站点(循环)

Block 3:
"vmovaps xmm0, xmm6"
call 0x1400023e0 <erff>
Block 4:
inc ebx
"vaddss xmm6, xmm6, xmm0"
"cmp ebx, 0x5f5e100"
jl 0x14000103f <Block 3>
Run Code Online (Sandbox Code Playgroud)

并在Windows上调用erf函数的开头

Block 1:
push rbp
"sub rsp, 0x40"
"lea rbp, ptr [rsp+0x20]"
"lea rcx, …
Run Code Online (Sandbox Code Playgroud)

c++ assembly x86-64 icc intel-vtune

65
推荐指数
1
解决办法
3792
查看次数

64位平台的效率:指针与32位数组索引

在他的一个主题演讲中,Andrei Alexandrescu建议,在64位平台上,使用32位数组索引比使用原始指针更快:

第16页:http://www.slideshare.net/andreialexandrescu1/three-optimization-tips-for-c-15708507

在他的Facebook帐户上,他更精确,并说:"更喜欢数组索引到指针(这个似乎每十年逆转一次)."

我已经尝试了许多方法来找到差异,但我还没有设法构建任何显示这种差异的程序.知道安德烈,我不会感到惊讶,差异不超过百分之几,但如果有人找到这样的例子,我会很高兴.

这是我做过的测试.我选择n = 5000,足够大以获得合适的时序并且足够小以使一切都适合L1缓存.我循环了几次,以便CPU频率上升.

#include <iostream>
#include <chrono>

int main(int argc, const char* argv[]) {
  const int n{5000};
  int* p{new int[n]};

  // Warm up the cache
  for (int i{0}; i < n; i++) {
    p[i] += 1;
  }

  for (int j{0}; j < 5; j++) {
    {
      auto start_pointer = std::chrono::high_resolution_clock::now();
      for (int* q{p}; q != p + n; ++q) {
        ++(*q);
      }
      auto end_pointer = std::chrono::high_resolution_clock::now();
      auto time_pointer = std::chrono::duration_cast<std::chrono::nanoseconds>(
                              end_pointer - …
Run Code Online (Sandbox Code Playgroud)

c++ arrays performance pointers

38
推荐指数
3
解决办法
2805
查看次数

内联方法:内部对外部类定义

如果你有一个方法,并且你想给编译器一个暗示它是一个好主意内联它,你目前有两个解决方案.第一个是在声明类时定义方法:

class Vector {
private:
    double* data_;
    double* size_;
    double* capacity_;
public:
    double& operator[](int k) {
        return data_[k];
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

由于此方法可能会降低可读性,另一种解决方案是使用inline关键字并在类外定义方法:

class Vector {
private:
    double* data_;
    double* size_;
    double* capacity_;
public:
    inline double& operator[](int k);
    ...
}

double& Vector::operator[](int k) {
    return data_[k];
}
Run Code Online (Sandbox Code Playgroud)

这使得代码更具可读性(至少我更喜欢它).阅读我的STL实现,我发现他们使用了两者的混合.一些方法(我认为应该真正内联的方法)在类中定义,而其他方法则使用inline关键字在类外定义.该文件也以该类的注释声明开头.

所以我的问题如下.当前的编译器(我正在考虑GCC,Clang,Intel和Visual Studio)更有可能内联在类中声明的成员函数,而不是使用inline关键字在类外声明的成员函数?

备注:这个问题与我何时应该为函数/方法编写关键字'inline'不重复我的问题是关于编译器实现.这两种方式表明你希望这些函数被内联是等价的.STL的编写方式表明它们不是.

c++ inline

19
推荐指数
3
解决办法
5976
查看次数

std :: vector <char>的max_size()有错误吗?

std::vector< char >::max_size()n = 32和n = 64位系统的测试结果感到困惑.结果是2 n - 1.让我解释为什么我感到困惑.

每执行std::vector<T>,我知道有一个类型的三个成员T*:begin_,end_,capacity_.

begin_指向向量的第一个值并end_指向最后一个向量的值.因此,矢量的大小由下式给出end_ - begin_.但是这种差异的结果是在我知道的每个实现上都是nstd::ptrdiff_t有符号整数的类型.

因此,这种类型不能存储2 ñ - 1,但仅达2 ñ - 1 - 1.如果你看看你std::vector实现,你会清楚地看到,尺寸使得它转换成一无符号之前的2个指针(差别整数).

那么,为什么他们可以假装存储超过2 n -1个元素而不会破坏.size()

c++ vector

18
推荐指数
1
解决办法
470
查看次数

施工中的自我参考

我刚刚发现以下代码与gcc 5.4和Intel编译器18.0.2一起编译.Clang 6.0.0只是发出警告.

#include <vector>

int main() {
  std::vector<double> v = v;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的代码中有一个非常相似的错误,我担心这些代码可以编译.我的问题是:

  • 它是合法的C++吗?如果是的话,该怎么办?
  • 如何在编译时捕获这些"错误"?

c++

17
推荐指数
1
解决办法
370
查看次数

具有价值语义的表现

我非常关注代码的性能和可读性,我从Google的Chandler Carruth那里得到了我的大部分想法.我想在不丢失性能的情况下为C++应用以下规则来实现干净的代码.

  • 将所有内置类型作为值传递
  • 传递所有不想通过const引用变异的对象
  • 传递函数需要按值消耗的所有对象
  • 禁止其他一切.在拐角情况下,传递指针.

这样,功能没有副作用.这是代码可读性的必要条件,并使C++成为一种功能.现在来了表现.如果你想编写一个为std :: vector的每个元素添加1的函数,你能做什么?这是我的解决方案.

std::vector<int> add_one(std::vector<int> v) {
    for (std::size_t k = 0; k < v.size(); ++k) {
        v[k] += 1;
    }
    return v;
}

...
v = add_one(std::move(v));
...
Run Code Online (Sandbox Code Playgroud)

我发现这非常优雅,只有2个动作.这是我的问题:

  • 这是合法的C++ 11吗?
  • 你认为这个设计有什么缺点吗?
  • 编译器无法自动将v = f(v)转换为此?一种复制省略.

PS:人们问我为什么不喜欢通过参考.我有两个参数:

1 - 它没有在调用站点明确指出哪个参数可能会发生变异.

2 - 它有时会扼杀性能.由于别名,引用和指针是编译器的地狱.我们来看下面的代码

std::array<double, 2> fval(const std::array<double, 2>& v) {
    std::array<double, 2> ans;
    ans[0] = cos(v[0] + v[1]);
    ans[1] = sin(v[0] + v[1]);
    return ans;
}
Run Code Online (Sandbox Code Playgroud)

将ans作为参考的相同代码慢了2倍:

std::array<double, 2> fref(const std::array<double, 2>& v,
        std::array<double, …
Run Code Online (Sandbox Code Playgroud)

c++ move c++11

11
推荐指数
1
解决办法
505
查看次数

在std :: vector上调整大小不会调用移动构造函数

我一直在使用std :: vector来理解何时构造,破坏,复制构造和移动构造的对象.为此,我编写了以下程序

#include <iostream>
#include <vector>

class Test {
public:
    Test() {
        std::cout << "Constructor called for " << this << std::endl;
    }
    Test(const Test& x) {
        std::cout << "Copy Constructor called for " << this << std::endl;
    }
    Test(Test&& x) {
        std::cout << "Move Constructor called for " << this << std::endl;
    }
    ~Test() {
        std::cout << "Destructor called for " << this << std::endl;
    }
};

int main() {
    std::vector<Test> a( 1 );
    a.resize(3);

    return 0;
} …
Run Code Online (Sandbox Code Playgroud)

c++ move vector c++11

8
推荐指数
1
解决办法
1118
查看次数

内存分配由编译器优化

在他的演讲"效率与算法,性能与数据结构"中,Chandler Carruth谈到了在C++中需要更好的分配器模型.当前的分配器模型侵入了类型系统,因此很难在许多项目中工作.另一方面,Bloomberg分配器模型不会入侵类型系统,而是基于虚函数调用,这使得编译器无法"看到"分配并对其进行优化.在他的演讲中,他谈到编译器重复删除内存分配(1:06:47).

我花了一些时间来找到一些内存分配优化的例子,但我发现这个代码示例,在clang下编译,优化掉所有的内存分配,只返回1000000而不分配任何东西.

template<typename T>
T* create() { return new T(); }

int main() {
    auto result = 0;
    for (auto i = 0; i < 1000000; ++i) {
        result += (create<int>() != nullptr);
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

以下论文http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html也说分配可以在编译器中融合,似乎表明一些编译器已经做了那种排序东西的.

由于我对高效内存分配的策略非常感兴趣,我真的很想理解为什么Chandler Carruth反对Bloomberg模型中的虚拟调用.上面的例子清楚地表明,clang可以在看到分配时优化.

  1. 我希望有一个"真实代码",这个优化是有用的,并且由任何当前的编译器完成
  2. 您是否有任何代码示例,其中不同的分配由anu当前编译器融合?
  3. 你是否理解Chandler Carruth在1:06:47他的讲话中说编译器可以"重复删除"你的分配时的含义?

c++ memory optimization memory-management allocation

8
推荐指数
1
解决办法
1007
查看次数

Google如何处理C++中的错误

Google不会在其C++代码库中使用异常.对于错误,它们使用一个名为status的类,而不是程序员从函数返回时必须检查的类.否则程序无法编译(在41:34 链接https://www.youtube.com/watch?v=NOCElcMcFik).我有几个问题:

1)网上是否有免费提供该课程的例子?

2)对于"void f()",可以使用可以转换成"状态f()"的副作用.但是如果你的函数已经返回一个值怎么办?Google不允许传递非const的引用,因此您无法改变提供给您的Status对象.那他们怎么办?

谢谢你的帮助.

c++ exception

7
推荐指数
1
解决办法
317
查看次数