- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
只是好奇哪个在 swift 中更高效/更好:
也许通过一个例子可以更好地解释这一点:
var one = Object()
var two = Object()
var three = Object()
func firstFunction() {
let tempVar1 = //calculation1
one = tempVar1
let tempVar2 = //calculation2
two = tempVar2
let tempVar3 = //calculation3
three = tempVar3
}
func seconFunction() {
var tempVar = //calculation1
one = tempVar
tempVar = //calculation2
two = tempVar
tempVar = //calculation3
three = tempVar
}
这两个函数哪个更高效?感谢您的宝贵时间!
最佳答案
不要太可爱,但上面代码的最有效版本是:
var one = Object()
var two = Object()
var three = Object()
这在逻辑上等同于您编写的所有代码,因为您从不使用计算结果(假设计算没有副作用)。优化器的工作就是简化为这种最简单的形式。从技术上讲,最简单的形式是:
func main() {}
但优化器并不那么聪明。但是优化器确实足够聪明,可以得到我的第一个例子。考虑这个程序:
var one = 1
var two = 2
var three = 3
func calculation1() -> Int { return 1 }
func calculation2() -> Int { return 2 }
func calculation3() -> Int { return 3 }
func firstFunction() {
let tempVar1 = calculation1()
one = tempVar1
let tempVar2 = calculation2()
two = tempVar2
let tempVar3 = calculation3()
three = tempVar3
}
func secondFunction() {
var tempVar = calculation1()
one = tempVar
tempVar = calculation2()
two = tempVar
tempVar = calculation3()
three = tempVar
}
func main() {
firstFunction()
secondFunction()
}
通过优化的编译器运行它:
$ swiftc -O -wmo -emit-assembly x.swift
这是整个输出:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 9
.globl _main
.p2align 4, 0x90
_main:
pushq %rbp
movq %rsp, %rbp
movq $1, __Tv1x3oneSi(%rip)
movq $2, __Tv1x3twoSi(%rip)
movq $3, __Tv1x5threeSi(%rip)
xorl %eax, %eax
popq %rbp
retq
.private_extern __Tv1x3oneSi
.globl __Tv1x3oneSi
.zerofill __DATA,__common,__Tv1x3oneSi,8,3
.private_extern __Tv1x3twoSi
.globl __Tv1x3twoSi
.zerofill __DATA,__common,__Tv1x3twoSi,8,3
.private_extern __Tv1x5threeSi
.globl __Tv1x5threeSi
.zerofill __DATA,__common,__Tv1x5threeSi,8,3
.private_extern ___swift_reflection_version
.section __TEXT,__const
.globl ___swift_reflection_version
.weak_definition ___swift_reflection_version
.p2align 1
___swift_reflection_version:
.short 1
.no_dead_strip ___swift_reflection_version
.linker_option "-lswiftCore"
.linker_option "-lobjc"
.section __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
.long 0
.long 1088
您的函数甚至在 输出中,因为它们什么都不做。 main
简化为:
_main:
pushq %rbp
movq %rsp, %rbp
movq $1, __Tv1x3oneSi(%rip)
movq $2, __Tv1x3twoSi(%rip)
movq $3, __Tv1x5threeSi(%rip)
xorl %eax, %eax
popq %rbp
retq
这会将值 1、2 和 3 粘贴到全局变量中,然后退出。
我的观点是,如果它足够聪明可以做到这一点,请不要尝试用临时变量来事后猜测它。它的工作是弄清楚这一点。事实上,让我们看看它有多聪明。我们将关闭整体模块优化 (-wmo
)。没有它,它不会剥离函数,因为它不知道是否有其他东西会调用它们。然后我们可以看看它是如何编写这些函数的。
这是firstFunction()
:
__TF1x13firstFunctionFT_T_:
pushq %rbp
movq %rsp, %rbp
movq $1, __Tv1x3oneSi(%rip)
movq $2, __Tv1x3twoSi(%rip)
movq $3, __Tv1x5threeSi(%rip)
popq %rbp
retq
因为它可以看到计算方法只返回常量,所以它内联这些结果并将它们写入全局变量。
现在 secondFunction()
怎么样:
__TF1x14secondFunctionFT_T_:
pushq %rbp
movq %rsp, %rbp
popq %rbp
jmp __TF1x13firstFunctionFT_T_
是的。太聪明了。它意识到 secondFunction()
与 firstFunction()
相同,它只是跳转到它。您的函数完全相同,优化器知道这一点。
那么什么是最有效的呢?最容易推理的那个。副作用最少的一种。最容易阅读和调试的一种。这就是您应该关注的效率。让优化器完成它的工作。它真的很聪明。你用漂亮、清晰、明显的 Swift 编写得越多,优化器就越容易完成它的工作。每次你“为了性能”做一些聪明的事情,你只是让优化器更加努力地工作来弄清楚你做了什么(并且可能撤销它)。
只是为了结束这个想法:您创建的局部变量几乎没有提示给编译器。当编译器将您的代码转换为它的内部表示 (IR) 时,编译器会生成它自己的 局部变量。红外在 static single assignment form (SSA),其中每个变量只能赋值一次。因此,您的第二个函数实际上创建了比第一个函数更多的局部变量。这是函数一(使用 swiftc -emit-ir x.swift
创建):
define hidden void @_TF1x13firstFunctionFT_T_() #0 {
entry:
%0 = call i64 @_TF1x12calculation1FT_Si()
store i64 %0, i64* getelementptr inbounds (%Si, %Si* @_Tv1x3oneSi, i32 0, i32 0), align 8
%1 = call i64 @_TF1x12calculation2FT_Si()
store i64 %1, i64* getelementptr inbounds (%Si, %Si* @_Tv1x3twoSi, i32 0, i32 0), align 8
%2 = call i64 @_TF1x12calculation3FT_Si()
store i64 %2, i64* getelementptr inbounds (%Si, %Si* @_Tv1x5threeSi, i32 0, i32 0), align 8
ret void
}
在这种形式中,变量有一个%
前缀。如您所见,有 3 个。
这是你的第二个函数:
define hidden void @_TF1x14secondFunctionFT_T_() #0 {
entry:
%0 = alloca %Si, align 8
%1 = bitcast %Si* %0 to i8*
call void @llvm.lifetime.start(i64 8, i8* %1)
%2 = call i64 @_TF1x12calculation1FT_Si()
%._value = getelementptr inbounds %Si, %Si* %0, i32 0, i32 0
store i64 %2, i64* %._value, align 8
store i64 %2, i64* getelementptr inbounds (%Si, %Si* @_Tv1x3oneSi, i32 0, i32 0), align 8
%3 = call i64 @_TF1x12calculation2FT_Si()
%._value1 = getelementptr inbounds %Si, %Si* %0, i32 0, i32 0
store i64 %3, i64* %._value1, align 8
store i64 %3, i64* getelementptr inbounds (%Si, %Si* @_Tv1x3twoSi, i32 0, i32 0), align 8
%4 = call i64 @_TF1x12calculation3FT_Si()
%._value2 = getelementptr inbounds %Si, %Si* %0, i32 0, i32 0
store i64 %4, i64* %._value2, align 8
store i64 %4, i64* getelementptr inbounds (%Si, %Si* @_Tv1x5threeSi, i32 0, i32 0), align 8
%5 = bitcast %Si* %0 to i8*
call void @llvm.lifetime.end(i64 8, i8* %5)
ret void
}
这个有 6 个局部变量!但是,就像原始源代码中的局部变量一样,这并没有告诉我们最终的性能。编译器之所以创建这个版本,是因为它比变量可以更改其值的版本更容易推理(因此更容易优化)。
(更引人注目的是 SIL 中的这段代码 (-emit-sil
),它为函数 1 创建了 16 个局部变量,为函数 2 创建了 17 个局部变量!如果编译器乐于发明 16局部变量只是为了让它更容易推理大约 6 行代码,你当然不应该担心你创建的局部变量。它们不仅仅是一个小问题;它们是完全免费的。)
关于swift - 哪个更有效 : Creating a "var" and re-using it, 或创建多个 "let"s?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45108973/
假设有一个创建用户的操作。如果存在指定的电子邮件或用户名,此操作可能会失败。如果它失败了,则需要确切地知道原因。在我看来,有三种方法可以做到这一点,我想知道是否有明显的赢家。 所以,这是一个类用户:
var obj1 = Object.create; console.log(typeof obj1); var obj2 = Object.create(null); console.log(type
I am getting this error after running npm run build yield User.create({^在运行NPM Run Build Year Use
我应该为其他人将从中继承的第一个父对象传递哪个参数,哪个参数更有效 Object.create(Object.prototype) Object.create(Object) Object.creat
我正在尝试使用 JDBC(最新版本)设置 SQL Server 2008 数据库。 我有一个我想一起执行的 setup sql 命令列表: 基本上我做的是: connection.setAutoCom
我正在尝试创建一个 CloudFormation 模板来创建一个 Auto Scaling 组,以便我可以从中启动 2 个实例。 我已经创建了 Auto Scaling 组,但我不知道如何编写用于从
我正在创建我的第一个WordPress网站。我已经在我的网站上安装了Elementor Pro插件。随之而来的一个有利因素是“循环旋转木马”。。。当我把它添加到我的页面时,一切似乎都在工作,但是当我点
create-react-app error 我从终端运行yarn start时收到此错误消息...我尝试了sudo killall node和许多其他过程来清除i-node却没有成功。 我也将我的c
在 CRM 中,当我尝试设置工作流程时,我可以选择与某个实体的创建时间相关的超时。涉及三个字段。 记录创建于 创建于 修改时间 虽然最后一个很明显,但我看不出其他两个之间有任何逻辑上的区别。 最佳答案
我在一次采访中被问到这个问题。我无法回答。 "browserslist": [ ">0.2%", "not dead", "not ie <= 11", "not op_mini all" ] 我可以
这是一个 Rails 新手问题: 当我在模型上调用 create() 时,它会绕过关联的 Controller create 操作吗? 例如,这没有命中我的标签 Controller #create
我不明白这两种特权之间的区别。 我找到了这两种解释,但对我没有帮助。 CREATE TABLE -> Enables a user to create a table owned by that us
我是 SharePoint 工作流的新手。 创建新任务并分配 TaskId 时,我有两个选项: 创建一个新字段来保存 TaskId 创建一个新属性来保存 TaskId。 新属性是一个依赖属性。 我的问
我突然注意到我们的代码库中有一个TDataModuleTestExchange(nil)“构造函数调用”: procedure TDialoogConfigExchange.ButtonTestCli
我有一个具有 TComponent 变量的单元,我在单元初始化时创建此组件,如下所示: var XComp: TComponent; . . . . initialization begin
Composer 是否提供了更新项目创建时使用的包的方法?即,如果我使用以下内容创建一个新的 Laravel 项目 composer create-project --prefer-dist lara
在 Hibernate 中,如果我们将 hbm2ddl.auto 设置为 create/create-drop ,那么它将在启动时删除旧模式并创建新模式。这意味着,它也会删除数据?..我的疑问是,如果
我使用了 create an Automated Build 中的此链接 ( this guide ) . 浏览器错误控制台显示: https://hub.docker.com/v2/reposito
我已经搜索了 msdn 并没有找到答案。我应该知道有什么区别吗? 如果真的没有区别,那么为什么会存在这种冗余? --SQL Server Stored Procedure Syntax CREATE
我有以下内容: var CardViewModel = function (data) { ko.mapping.fromJS(data, {}, this); this.editin
我是一名优秀的程序员,十分优秀!