附注:这确实是实用的...我非常清楚如何以合理的方式提高代码性能(改进算法,在优化之前进行分析等)。
根据我的经验,最不正统的优化C代码的方法是对应用程序进行分析,识别性能较慢的结构或数据库操作,然后使用大O分析设计合理的解决方案。
使用内联汇编?
说真的,如果仅通过更改C代码就能提高性能,那么很有可能可以干净地完成。
一些例外:
1)如果您经常依赖于不同类型指针的对齐语义,则通常可以在指针上执行块操作,这些操作从技术上讲会暴露您的应用程序以超出边界的条件,但实际上由于系统的对齐特征而不会。 因此,可以通过对齐初始字符,然后使用long *指针来执行内部块的内存复制。
2)如果您知道编译器分配本地变量的内存顺序,则可能可以巧妙地复制堆栈帧。 这可能允许您实现协程,而语言本身不支持。 协程通常是实现某些类型的循环控制的一种更简单,更快速的方法。
3)无论如何使用,联合体始终有点“hacky”。 这是一种使用相当宽松的类型检查来实现多态性的方法。
4)将C预处理器用作自动生成代码的方式通常非常难以调试和阅读。 因此,人们倾向于避免使用它。
对你的代码进行剖析,找出慢的地方,并使用内联汇编来优化它们。
1) 循环展开。如果不需要循环,每次迭代可以省略跳转、比较和增量操作,从而提高效率。
2) 避免双重间接寻址。通常情况下,执行算术运算比检索更快,因此a[y*height+x]相对于a[y][x]来说更快,并且大小为MxN的一维数组相对于尺寸为MxN的矩形矩阵可以节省M(或N)个指针字长。
3) 在可能的情况下使用荒谬的汇编优化。例如,在x86架构上,您可以使用BSWAP指令以一次操作交换字节,而不是正常的temp=a; a=b; b=temp;
模式。
当然,别忘了:
4) 不要进行边界检查或错误处理。
话虽如此,实际应用中我建议避免使用这些技巧,除了(2)。
您正在寻找一种非正统、无限制但通用的解决方案来优化C语言?
那就用汇编语言重写吧。
对于C代码的性能,已经没有什么非正统的方法可用了。所有有效的技术都已经“正统化”。
我发现最好的方法是使用具有CPU性能计数器访问权限的分析工具,并特别关注缓存和分支未命中。在任何地方添加缓存预取并尽可能删除不可预测的分支。
不要费心进行循环展开。如果分支是可预测的,则几乎是免费的。让编译器自己处理。
在某些非常并行的体系结构上,例如IA64,将循环展开到最后可能更快。一个例子是避免使用C字符串函数。使用memset将字符串数组清零,使用memcpy设置字符串,使用memcmp将整个数组与另一个类似的数组进行比较。这可以使用64位加载,永远不必检查零终止符,并且如果使用64或128的“小”数组大小,则可以优化为根本不需要循环或分支。 memxxx()函数通常是编译器内置的,并且非常优化。
在Dathan的答案中,对于第三点,还有一种交换变量的非常规方法,可以使用异或运算符。
int = 3, y = 4; x = x ^ y; y = y ^ x; x = x ^ y;
现在x和y已经被交换了! :)
另外,当你将某些东西除以2时,最好使用右移运算符。同样的道理也适用于乘以2时的左移运算符。
在旧的Borland C编译器中,有一个_stklen
属性,可以分配它以减少堆栈大小和代码。由于编译器技术得到了提升,我现在没有看到类似的东西。
使用malloc时,最好使用calloc,因为它会将内存初始化为零。
使用三元运算符而不是if / else语句显然更快,我想编译器编写人员在机器代码生成方面变得更加聪明了。我无法提供这方面的证明,但在Borland C 3.01统治时期,这是真实的。
使用汇编程序嵌入代码。
我喜欢这个问题的主题,因为它让我想起了过去,当时内存很宝贵,必须将一品脱装入一个夸脱壶中,并使用x86代码的咒语。感谢您发布这个问题,Database先生。
您的编译器几乎肯定比您丑陋的尝试更擅长优化。大多数历史上使用的小技巧现在都毫无意义。忽略可读性和可维护性的人往往会写出效率较低的代码,因为真正的优化变得更加困难。
当代码已经经过所有可能的优化且仍需要提高性能时,将关键部分重写为ASM是唯一有希望产生任何效果的方法。