在研究各种编译器的各种代码段的输出,我已经注意到,英特尔的C编译器(ICC)具有很强的偏爱发射一对趋势NEG+ ADD指令,其中其他的编译器将使用一个单一的SUB指令.
举个简单的例子,考虑以下C代码:
uint64_t Mod3(uint64_t value)
{
return (value % 3);
}
Run Code Online (Sandbox Code Playgroud)
ICC将其转换为以下机器代码(无论优化级别如何):
mov rcx, 0xaaaaaaaaaaaaaaab
mov rax, rdi
mul rcx
shr rdx, 1
lea rsi, QWORD PTR [rdx+rdx*2]
neg rsi ; \ equivalent to:
add rdi, rsi ; / sub rdi, rsi
mov rax, rdi
ret
Run Code Online (Sandbox Code Playgroud)
而其他编译器(包括MSVC,GCC和Clang)都将生成基本相同的代码,除了NEG+ ADD序列被单个SUB指令替换.
就像我说的,这不仅仅是ICC如何编写这个特定片段的怪癖.这是我在分析算术运算的反汇编时反复观察到的模式.我通常不会考虑这个,除了ICC是一个非常好的优化编译器,它是由拥有有关其微处理器的内幕信息的人开发的.
英特尔是否知道有关SUB其处理器上指令的实现的信息,将其分解为NEG+ ADD指令会更加优化?使用RISC样式的指令解码成更简单的μops是现代微体系结构的众所周知的优化建议,因此有可能SUB在内部分解为单个NEG和ADDμops,并且前端解码器使用这些实际上更有效"更简单"的指示?现代CPU很复杂,所以一切皆有可能.
默认的放置new运算符在18.6 [support.dynamic]1中声明,带有非抛出异常规范:
void* operator new (std::size_t size, void* ptr) noexcept;
Run Code Online (Sandbox Code Playgroud)
这个函数没有任何作用,除非return ptr;它是合理的noexcept,但是根据5.3.4 [expr.new]15这意味着编译器必须检查它在调用对象的构造函数之前不返回null:
-15-
[ 注意:除非使用非抛出异常规范(15.4)声明分配函数,否则它表示无法通过抛出std::bad_alloc异常来分配存储(第15,18.6.2.1节); 否则返回非空指针.如果使用非抛出异常规范声明分配函数,则返回null以指示无法分配存储,否则返回非空指针.-end note ]如果分配函数返回null,则不进行初始化,不应调用解除分配函数,并且new-expression的值应为null.
在我看来(特别是对于放置new,而不是一般)这个空检查是一个不幸的性能命中,尽管很小.
我一直在调试一些代码,其中new在一个性能敏感的代码路径中使用了放置,以改进编译器的代码生成,并在程序集中观察到null检查.通过提供new使用抛出异常规范声明的特定于类的放置重载(即使它不可能抛出),删除了条件分支,这也允许编译器为周围的内联函数生成更小的代码.说放置new函数的结果可能会抛出,即使它不能,也是可测量的更好的代码.
所以我一直想知道是否真的需要进行空检查new.它返回null的唯一方法是将它传递给null.尽管写下来是可能的,而且显然是合法的:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
Run Code Online (Sandbox Code Playgroud)
我不明白为什么这将是有益的,我认为它会更好,如果程序员有明确使用放置前检查null new如
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
Run Code Online (Sandbox Code Playgroud)
有没有人需要放置new来正确处理空指针的情况?(即不添加作为ptr有效内存位置的显式检查.)
我想知道禁止将空指针传递给默认的放置 …
我写了两种方法来检查性能
public class Test1 {
private String value;
public void notNull(){
if( value != null) {
//do something
}
}
public void nullNot(){
if( null != value) {
//do something
}
}
}
Run Code Online (Sandbox Code Playgroud)
并在编译后检查它的字节码
public void notNull();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: getfield #2; //Field value:Ljava/lang/String;
4: ifnull 7
7: return
LineNumberTable:
line 6: 0
line 9: 7
StackMapTable: number_of_entries = 1
frame_type = 7 /* same */
public void nullNot();
Code:
Stack=2, Locals=1, Args_size=1
0: aconst_null
1: …Run Code Online (Sandbox Code Playgroud) 是否有可能通过使用纯位加法,减法除以10的无符号整数,也许繁衍?使用资源非常有限且速度慢的处理器.
可能重复:
在Java中使用final关键字可以提高性能吗?
在最后的修改有不同的后果取决于你把它应用到什么在java中.我想知道的是,如果另外它可能有助于编译器创建更高效的字节码.我想这个问题深入探讨了JVM的工作原理以及JVM的具体情况.
那么,根据您的专业知识,请执行以下任何一项帮助编译器,或者您是否仅出于正常的java原因使用它们?
谢谢!
编辑:谢谢你的所有答案!请注意,正如@Zohaib建议的那样,我的问题与此重复.在发布之前我搜索得不够好.我不是删除它,因为你们做出了很好的贡献,但答案可以合并.除非另有说明,否则我会让"投票结束"系统决定.
我正在寻找最快/最节省空间的方法,将 64 位寄存器减少为 32 位寄存器,仅保留 64 位寄存器的零/非零状态。
我目前适用于所有值的最佳想法是popcntq
(1c tput,主流英特尔上的 3c 延迟,5 字节代码大小):
// rax is either zero or non-zero
popcntq %rax, %rax
// eax will be zero if rax was zero, otherwise it will be non-zero
Run Code Online (Sandbox Code Playgroud)
注意:直接使用 32 位是行不通的eax:如果rax说 的2^61零/非零状态eax与 的不同rax
有没有更好的巧妙方法?
我听过一位老师放弃了这一次,从那以后一直困扰着我.假设我们要检查整数x是否大于或等于0.有两种方法可以检查:
if (x > -1){
//do stuff
}
Run Code Online (Sandbox Code Playgroud)
和
if (x >= 0){
//do stuff
}
Run Code Online (Sandbox Code Playgroud)
根据这个老师>会稍快一点>=.在这种情况下它是Java,但据他说,这也适用于C,c ++和其他语言.这句话有什么道理吗?
c++ java operators premature-optimization micro-optimization
我有一些经过大量优化的数学函数需要1-2 nanoseconds完成.这些功能每秒被称为数亿次,因此尽管性能已经非常出色,但呼叫开销仍是一个问题.
为了保持程序的可维护性,提供这些方法的类继承了一个IMathFunction接口,以便其他对象可以直接存储特定的数学函数并在需要时使用它.
public interface IMathFunction
{
double Calculate(double input);
double Derivate(double input);
}
public SomeObject
{
// Note: There are cases where this is mutable
private readonly IMathFunction mathFunction_;
public double SomeWork(double input, double step)
{
var f = mathFunction_.Calculate(input);
var dv = mathFunction_.Derivate(input);
return f - (dv * step);
}
}
Run Code Online (Sandbox Code Playgroud)
由于消费代码使用它,这种接口与直接呼叫相比造成了巨大的开销.一个直接调用需要1-2ns,而虚拟接口调用需要8-9ns.显然,接口的存在及其随后的虚拟呼叫转换是这种情况的瓶颈.
如果可能的话,我想保留可维护性和性能.有没有办法在实例化对象时将虚函数解析为直接调用,以便所有后续调用都能够避免开销?我认为这将涉及用IL创建委托,但我不知道从哪里开始.
对于min(ctz(x), ctz(y)),我们可以使用ctz(x | y)来获得更好的性能。但是关于max(ctz(x), ctz(y))?
ctz表示“计数尾随零”。
C++ 版本(编译器资源管理器)
#include <algorithm>
#include <bit>
#include <cstdint>
int32_t test2(uint64_t x, uint64_t y) {
return std::max(std::countr_zero(x), std::countr_zero(y));
}
Run Code Online (Sandbox Code Playgroud)
Rust 版本(编译器资源管理器)
pub fn test2(x: u64, y: u64) -> u32 {
x.trailing_zeros().max(y.trailing_zeros())
}
Run Code Online (Sandbox Code Playgroud) 您是否注意到如果您在任何调用之前在脚本中设置实际时区,那么该date()函数的工作速度比平时快2倍date()?我对此非常好奇.
看看这段简单的代码:
<?php
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) date('Y-m-d H:i:s');
echo (microtime(true) - $start);
?>
Run Code Online (Sandbox Code Playgroud)
它只date()使用for循环100,000次调用函数.我得到的结果总是大约1.6秒(Windows,PHP 5.3.5),但......
如果我在开始之前再次设置相同的时区添加一条荒谬的行:
date_default_timezone_set(date_default_timezone_get());
Run Code Online (Sandbox Code Playgroud)
我得到的时间低于800毫秒 ; 快2倍(同一台服务器).
我正四处寻找这种行为的任何合理解释,但没有任何成功.从我的角度来看,这个额外的行没用,但PHP不同意我的看法.
我已经在两个Linux服务器(不同的PHP版本)上尝试了这个测试,并且得到了不同的结果时间,但比例为6:1.
注意:php.ini中的date.timezone属性已正确设置(欧洲/巴黎).
我在这里搜索相关问题并没有找到类似的东西.我还检查了date_default_time_zone()函数@php.net的手册,发现我不仅是一个注意到这一点的人,但仍然无法理解为什么会发生这种情况?
任何人?