我昨晚无法入睡,开始思考std::swap.这是熟悉的C++ 98版本:
template <typename T>
void swap(T& a, T& b)
{
T c(a);
a = b;
b = c;
}
Run Code Online (Sandbox Code Playgroud)
如果用户定义的类Foo使用外部资源,则效率低下.常用的习语是提供一种方法void Foo::swap(Foo& other)和一种专业化std::swap<Foo>.请注意,这不适用于类模板,因为您不能部分地专门化函数模板,并且重std命名命名空间中的名称是非法的.解决方案是在一个人自己的命名空间中编写模板函数,并依赖于参数依赖查找来查找它.这主要取决于客户端遵循" using std::swap成语"而不是std::swap直接调用.非常脆弱.
在C++ 0x中,如果Foo有一个用户定义的移动构造函数和一个移动赋值运算符,提供一个自定义swap方法和一个特化std::swap<Foo>几乎没有性能优势,因为C++ 0x版本std::swap使用高效的移动而不是副本:
#include <utility>
template <typename T>
void swap(T& a, T& b)
{
T c(std::move(a));
a = std::move(b);
b = std::move(c);
}
Run Code Online (Sandbox Code Playgroud)
不必再捣乱swap已经从程序员那里承担了很多负担.当前的编译器不会自动生成移动构造函数和移动赋值运算符,但据我所知,这将改变.剩下的唯一问题是异常安全,因为一般来说,允许移动操作,这会打开一大堆蠕虫.问题是"移动对象的状态究竟是什么?" 使事情进一步复杂化.
然后我在想,std::swap如果一切顺利的话,C++ 0x 中的语义究竟是什么?交换之前和之后的对象状态是什么?通常,通过移动操作进行交换不会触及外部资源,只会触及"平面"对象表示本身. …
我正在努力寻找有助于我的管理层理解对编译的C代码进行逆向工程的难易程度的事实.
之前在这个网站上已经提出了类似的问题(参见例如,是否可以"反编译"Windows .exe?或者至少查看程序集?或者可能反编译用C编写的DLL?),但这些问题的要点是反编译编译的C代码"很难,但并非完全不可能".
为了促进基于事实的答案,我包含了一个神秘函数的编译代码,我建议这个问题的答案通过它们是否可以确定这个函数的作用来衡量所提出技术的成败.这可能是不寻常的,但我认为这是获得这个工程问题的"良好主观"或事实答案的最佳方式.因此,你最好猜测这个功能在做什么,以及如何做?
这是使用gcc在Mac OSX上编译的已编译代码:
_mystery:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movsd LCPI1_0(%rip), %xmm1
subsd %xmm0, %xmm1
pxor %xmm2, %xmm2
ucomisd %xmm1, %xmm2
jbe LBB1_2
xorpd LCPI1_1(%rip), %xmm1
LBB1_2:
ucomisd LCPI1_2(%rip), %xmm1
jb LBB1_8
movsd LCPI1_0(%rip), %xmm1
movsd LCPI1_3(%rip), %xmm2
pxor %xmm3, %xmm3
movsd LCPI1_1(%rip), %xmm4
jmp LBB1_4
.align 4, 0x90
LBB1_5:
ucomisd LCPI1_2(%rip), %xmm1
jb LBB1_9
movapd %xmm5, %xmm1
LBB1_4:
movapd %xmm0, %xmm5
divsd %xmm1, %xmm5
addsd %xmm1, %xmm5
mulsd …Run Code Online (Sandbox Code Playgroud) 问题不在于Linux内核.它也不是C与C++的争论.
我做了一项研究,在我看来,C++在嵌入式系统的异常处理和内存分配方面缺乏工具支持:
为什么linux内核没有在C++中实现? 除了公认的答案,请参阅Ben Collins的回答.
"[...]任何为C++设计内核模块的人都是[...]
(b)无法看到他正在编写的C++偏见,实际上只是C"" - 整个C++异常处理事情从根本上被打破了.对于内核来说尤其突破.
- 任何喜欢隐藏内存分配等内容的编译器或语言都不是内核的好选择."
"不得使用AV规则208 C++例外"
异常处理和内存分配是C++显然缺乏工具支持的唯一要点(在此上下文中)?
要解决异常处理问题,必须提供时间限制,直到抛出异常后才捕获异常?
你能解释一下为什么内存分配是个问题吗?如何克服这个问题,必须做些什么?
正如我所看到的,在这两种情况下,必须在编译时为发生的非常重要的事情提供上限,并在运行时依赖于事物.
回答:
我对编译器实际优化的方式没有很多经验,不同级别之间有什么区别(例如-O2与-cc3对于gcc).因此,我不确定以下两个语句对于任意编译器是否相同:
for(i=0;i<10;++i){
variable1*variable2*gridpoint[i];
}
Run Code Online (Sandbox Code Playgroud)
和
variable3=variable1*variable2;
for(i=0;i<10;++i){
variable3*gridpoint[i];
}
Run Code Online (Sandbox Code Playgroud)
从处理时间的角度来看,只计算variable1和variable2的乘积,因为它们在循环中不会改变,这是有意义的.然而,这需要额外的内存,而且我不确定优化器对此开销的影响有多大.如果您从纸张/书籍中获得等式并希望将其转换为计算机可读的内容,则第一个表达式是最容易阅读的.但第二个可能是最快的 - 特别是对于在循环内有很多未变量的更复杂的方程式(我有一些非常令人讨厌的非线性微分方程,我希望在代码中是人类可读的).如果我将变量声明为常量,是否有任何变化?我希望我的问题对任意编译器都有意义,因为我同时使用gcc,Intel和Portland编译器.
我需要在循环中有效地将一些常量添加或乘以double类型的结果以防止下溢.例如,如果我们有int,则乘以2的幂会很快,因为编译器将使用位移.有效的double加法和乘法是否有一种常量形式?
编辑:似乎没有多少人理解我的问题,为我的邋iness道歉.我会添加一些代码.如果a是int,则(乘以2的幂)将更有效
int a = 1;
for(...)
for(...)
a *= somefunction() * 1024;
Run Code Online (Sandbox Code Playgroud)
比1024更换为1023时,如果我们想要添加到int,不确定什么是最好的,但这不是我感兴趣的.我对a双重情况感兴趣.什么是常量的形式(例如2的幂),我们可以有效地补充和繁衍双?常量是任意的,只需要足够大以防止下溢.
这可能不仅限于C和C++,但我不知道更合适的标签.
可能每个人都使用某种优化开关(在gcc的情况下,最常见的是-O2我相信).
但是gcc(以及VS,Clang等其他编译器)在这些选项的存在下真的做了什么?
当然没有明确的答案,因为它很大程度上取决于平台,编译器版本等.但是,如果可能的话,我想收集一套"经验法则".我什么时候应该考虑加速代码的一些技巧?何时我应该把工作留给编译器?
例如,对于不同的优化级别,编译器会在这样的(一点点artifficial ......)案例中走多远:
1)sin(3.141592)
//是否会在编译时进行评估,还是应该考虑一个查找表来加速计算?
2)int a = 0; a = exp(18), cos(1.57), 2;
//编译器是否会评估exp和cos,尽管不需要,因为表达式的值等于2?
3)
for (size_t i = 0; i < 10; ++i) {
int a = 10 + i;
}
Run Code Online (Sandbox Code Playgroud)
//编译器是否会跳过整个循环,因为它没有可见的副作用?
也许你可以想到其他的例子.
是否可以使用memcpy复制数组的一部分?
比方说,我们有一个10个整数的数组.我们可以创建一个新数组,并将最后5个整数复制到其中吗?
是否有其他内存/阵列复制/操作工具可用于c/c ++?
假设我有一些文件和流的内部框架.我有IOutputStream带接口类write(char const *buffer, size_t size)和flush().我有一个工具,称为Printer可以与任何IOutputStream后代实例一起使用.然后我有Printer & operator<<(T x)样式方法,其中T x是要写入的数据(或引用或指向它的指针).
例如,Printer & operator<<(int x)将转换x为字符串,并将引用的输出流的write(...)函数调用为real.
让我们看看问题!调用:printer << "appletree";.它叫Printer & operator<<(char const *s).对于这种用法,我必须调用一个strlen(s)来确定大小,然后我可以调用最后一步.这是相当疯狂的,因为我知道appletree编译时的长度.
这有什么好的做法吗?STL如何ostream与Titerals一起玩?
string a = "";
string b = {};
Run Code Online (Sandbox Code Playgroud)
我无法找到解释它们之间差异的好参考.编译器有不同的看法吗?那么为什么?
有人问我一个问题:在以下两种情况中,哪一项是最快的:
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 5; j++)
{
count++;
}
}
Run Code Online (Sandbox Code Playgroud)
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 10; j++)
{
count++;
}
}
Run Code Online (Sandbox Code Playgroud)
在这两个场景中,计数的最终值将为50.但我不确定哪一个会更快?我认为CASE II更快但不确定......
如果有人能对它有所了解,那就太好了.哪个更快,为什么?
我试图理解使用WidgetURef::setName(URef作为一个通用参考,由Scott Meyers创造的术语)与WidgedRRef::setName(RRef作为R值参考)的性能影响:
#include <string>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
class WidgetRRef {
public:
void setName(std::string&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
w_uref.setName("Adela Novak");
WidgetRRef w_rref;
w_rref.setName("Adela Novak");
}
Run Code Online (Sandbox Code Playgroud)
我很欣赏通用引用应该使用std::forward,但这只是一个(不完美的)示例,以突出有趣的位.
问题
在这个特定的例子中,使用一个实现与另一个实现的性能影响是什么?虽然WidgetURef需要类型扣除,但它在其他方面是相同的WidgetRRef,不是吗?至少在这种特殊情况下,在两种情况下,参数都是r-value引用,因此不会创建临时值.这个推理是否正确?
背景
这个例子来自Scott Meyers的"Effective Modern C++"(p.170)的第25项.根据这本书(假设我的理解是正确的!),采用通用引用的版本T&&不需要临时对象而另一个需要临时对象std::string&&.我真的不明白为什么.
请考虑以下代码段:
for(i=0;i<10;i+=2) // 1
for(i=0;i<2;i=i+2) // 2
Run Code Online (Sandbox Code Playgroud)
哪一个会更好用?
性能有什么不同吗?
c++ ×9
c ×5
c++11 ×2
optimization ×2
arrays ×1
assembly ×1
clang ×1
decompiling ×1
gcc ×1
literals ×1
loops ×1
memcpy ×1
nested-loops ×1
performance ×1
real-time ×1
stl ×1
string ×1
swap ×1
underflow ×1
visual-c++ ×1
x86 ×1