可以使用 char[] 作为参数、返回等来解决任何性能问题
Can using char[] as parameters, return, and etc do any performance issues?
第一个使C++代码更具可读性;我正在编程编译器,我给了它:
var swap = ( int x, y ) => { //Assign method that returns two ints, and gets two ints as parameter to variable named swap.
var NewX = y
var NewY = x
}
var increment = ( int x ) => {
var Result = x + 1
}
注意:函数返回其第一个字母大写的任何变量。 swap
可以像... = swap( x, y ).NewX
一样使用,但increment
可以用作... = increment( x )
。
经过一些优化后,它生成:(swap
和increment
实际函数而不是变量,并优化了swap
堆栈)
template<int BytesCount> struct rawdata { //struct from some header
char _[ BytesCount ];
inline char &operator[] (int index) {
return _[ index ];
}
};
//...
rawdata<8> generatedfunction0( rawdata<8> p ) { // var swap = ( int x, y ) => {
return{ p[ 4 ], p[ 5 ], p[ 6 ], p[ 7 ], p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] };
}
rawdata<4> generatedfunction1( rawdata<4> p ) { // var increment = ( int x ) => {
rawdata<4> r = { p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] };
++*( ( int* )&r[ 0 ] );
return r;
}
我几乎可以肯定++*( ( int* )&r[ 0 ] );
不会做无用的间接,但是return{ p[ 4 ], p[ 5 ], p[ 6 ], p[ 7 ], p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] };
呢?是否有任何来源可以保证它会优化它,就好像它是两个整数放入数组中,而不是 8 个或更多个逐字节放置的指令?我不是在谈论这个特殊情况,而是类似的东西。
如果取决于,那么我正在使用 GCC 来编译生成的代码。
是的,它可能会损害性能 - 但并非总是如此。问题在于对单个字节的显式访问。
"智能"编译器会识别出您访问连续内存并尝试对其进行优化。但是由于某种原因,这根本不适用于gcc,clang或icc(尚未测试msvc)。编译器优化器仍有改进的空间,IIRC标准没有要求任何优化。
掉期:
因此,让我们处理每个函数,从交换开始。为了完整起见,我又添加了 2 个函数,请参阅代码片段后面:
#include <stdint.h>
rawdata<8> genSWAP(rawdata<8> p)
{
return { p[ 4 ], p[ 5 ], p[ 6 ], p[ 7 ], p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] };
}
rawdata<8> genSWAPvar(rawdata<8> p)
{
return { p._[ 4 ], p._[ 5 ], p._[ 6 ], p._[ 7 ], p._[ 0 ], p._[ 1 ], p._[ 2 ], p._[ 3 ] };
}
rawdata<8> genSWAP32(rawdata<8> p)
{
rawdata<8> res = p;
uint32_t* a = (uint32_t*)&res[0];
uint32_t* b = (uint32_t*)&res[4];
uint32_t tmp = *a;
*a = *b;
*b = tmp;
return res;
}
-
genSWAP
:您的函数 -
genSWAPvar
:与您相同,但不使用您定义的operator[]
-
genSWAP32
:明确打包您的字节 32 位每 32 位
您可以在此处查看生成的 asm。
genSWAP
和 genSWAPvar
没有什么不同,这意味着重载operator[]
只是被优化出来了。但是,每个字节在内存中单独访问,也单独处理。这很糟糕,因为在 32 位架构上,处理器一次从内存加载 4 个字节(64 位架构为 8)。因此,简而言之,gcc/clang/icc 正在发出指令来对抗 32 位架构的真正可能性......
genSWAP32
效率更高,执行最少数量的加载(对于 32 位),并正确使用寄存器(请注意,对于 64 位架构,应该只能执行一次加载而不是 2 次)。
最后,一些真正的措施:在 Ideone 上,genSWAP32
的速度几乎快了 4 倍(这是有道理的,因为它有 2 个负载而不是 8 个和更少的计算指令)。
增量:
同样在这里,您的函数与"优化"函数:
rawdata<4> genINC(rawdata<4> p)
{
rawdata<4> r = { p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] };
++*( ( int* )&r[ 0 ] );
return r;
}
rawdata<4> genINC32(rawdata<4> p)
{
rawdata<4> res = p;
uint32_t* a = (uint32_t*)&res[0];
++*a;
return res;
}
生成的 asm 在这里。
对于 clang 和 icc,杀手锏不是增量,而是初始化,您可以在其中单独访问每个字节。 GCC 和 ICC 可能默认这样做,因为字节的顺序可能与0 1 2 3
不同。令人惊讶的是,clang 认识到字节的顺序并正确优化了这一点 - 没有性能差异。
然后发生了一些有趣的事情:genINC32
函数在 gcc 上较慢,但在 msvc 上更快(*我在rise4fun上没有看到永久链接按钮,所以去那里粘贴在 ideone 上测试的代码)。如果没有看到 msvc 生成的汇编程序并进行比较,我对此没有任何解释。
总之,虽然可以让编译器正确优化所有代码,但现在不要依赖它,所以如果不需要,不要单独访问每个字节。
- 进程退出,返回值3221226356写入系统( "cls" )。(已解决)
- cpp 检查抱怨危险使用 c_str(). c_str() 返回的值在本次调用后无效,如何解决?
- 必须使用尾随返回类型的示例,因为无法用旧方法解决问题
- 解决PNP在一段时间后返回坏的rvec和tvec
- 我正在写一个快速的形式,可以跟踪迭代次数.但是,计数每次返回0.我怎样才能解决这个问题
- dlsym() 解决方法返回类型
- 任何人都知道如何解决尝试将从MySQL数据库返回的行打印到文本文件(.txt)中的C++
- 如何解决错误PRJ0002:错误结果 -1073741515从"cl.exe"返回?
- 如何解决自动返回类型C++-continuation
- 如何解决自动返回类型-C++(模板)
- 可以使用 char[] 作为参数、返回等来解决任何性能问题
- 采用N个参数并返回N个值的高性能解决方案
- 具有可变返回类型的纯虚拟函数的功能-解决方法/设计
- 在没有移动函数的情况下返回不可复制对象的解决方法
- 返回值复制问题(以改进调试时间)-- 这里的解决方案是什么?
- 调用返回类型不完整的方法和无法解释的模板解决方案
- sizeof打包位域返回4,但我期望是1.它是怎么来的,我怎么能解决
- 需要解决的限制:抽象类不能用于返回类型
- 创建并返回字符串向量——最有效的解决方案
- C++变量生存期 -- 需要解决方法才能临时返回