首页 > 编程知识 正文

内联函数在什么时候展开,什么时候用内联函数?

时间:2023-05-04 13:43:41 阅读:223325 作者:4631

内联函数什么时候展开

You know all of those Util files you create with all sorts of small functions that you end up using a lot throughout your app? If your utility functions get other functions as parameters, chances are you can improve the performance of your app by saving some extra object allocations, that you might not even know you’re making, with one keyword: inline. Let’s see what happens when you pass these short functions around, what inline does under the hood and what you should be aware of when working with inline functions.

您知道使用各种小功能创建的所有Util文件,最终在整个应用程序中大量使用这些文件吗? 如果您的实用程序函数将其他函数用作参数,则可以通过使用一个关键字inline来保存一些甚至可能不知道自己正在做的对象分配,从而提高应用程序的性能。 让我们看看当您传递这些短函数时会发生什么,内联在幕后做什么,以及在使用内联函数时应注意的事项。

函数调用-内幕 (Function call — under the hood)

Let’s say that you use SharedPreferences a lot in your app so you create this utility function to reduce the boilerplate every time you write something in your SharedPreferences:

假设您在应用程序中使用了很多SharedPreferences ,因此您创建此实用程序函数可以在每次在SharedPreferences编写内容时减少样板:

fun SharedPreferences.edit(
commit: Boolean = false,
action: SharedPreferences.Editor.() -> Unit
) {
val editor = edit()
action(editor)
if (commit) {
editor.commit()
} else {
editor.apply()
}
}

Then, you can use it to save a String token:

然后,您可以使用它保存String令牌:

private const val KEY_TOKEN = “token”class PreferencesManager(private val preferences: SharedPreferences){
fun saveToken(token: String) {
preferences.edit { putString(KEY_TOKEN, token) }
}
}

Now let’s see what’s going on under the hood when preferences.edit is called. If we look at the Kotlin bytecode (Tools > Kotlin > Decompiled Kotlin to Java) we see that there’s a NEW called, so a new object is being created, even if in our code we didn’t call any object constructor:

现在,让我们看看调用preferences.edit情况。 如果查看Kotlin字节码(“工具”>“ Kotlin”>“将Kotlin编译为Java”),我们会看到有一个NEW调用,因此正在创建一个新对象,即使在我们的代码中我们没有调用任何对象构造函数:

NEW com/example/inlinefun/PreferencesManager$saveToken$1

Let’s check the decompiled code to make this a bit friendlier. Our saveToken decompiled function is as follows (comments and formatting mine):

让我们检查一下反编译的代码,以使其更加友好。 我们的saveToken反编译功能如下(注释和格式化我的代码):

Each high-order function we create leads to a Function object creation and memory allocation that introduces runtime overhead.

我们创建的每个高阶函数都会导致Function对象的创建和内存分配,从而引入运行时开销。

内联功能-引擎盖下 (Inline function — under the hood)

To improve the performance of our app we can avoid the new function object creation, by using the inline keyword:

为了提高应用程序的性能,我们可以使用inline关键字来避免创建新的函数对象:

inline fun SharedPreferences.edit(
commit: Boolean = false,
action: SharedPreferences.Editor.() -> Unit
) { … }

Now the Kotlin bytecode doesn’t contain any NEW calls and here’s how the decompiled java code looks like for our saveToken method (comments and formatting mine):

现在Kotlin字节码不包含任何NEW调用,这是我们saveToken方法的反编译Java代码的样子(注释和格式化我的代码):

Because of the inline keyword, the compiler copies the content of the inline function to the call site, avoiding creating a new Function object.

由于使用inline关键字,编译器将inline函数的内容复制到调用站点,从而避免了创建新的Function对象。

标记为内联的内容 (What to lmdjz as inline)

⚠️ If you’re trying to lmdjz as inline a function that doesn’t accept another function as a parameter, you won’t get significant performance benefits and the IDE will even tell you that, suggesting you to remove it:

If️如果您尝试将不接受另一个函数作为参数的函数标记为内联函数,则不会获得明显的性能优势,IDE甚至会告诉您,建议您删除它:

⚠️ Because inlining may cause the generated code to grow, make sure that you avoid inlining large functions. For example, if you check the Kotlin Standard Library, you’ll see that most of the inlined functions have only 1–3 lines.

⚠️ 因为内联可能导致生成的代码增长,所以请确保您 避免内联大型函数 。 例如,如果您查看Kotlin标准库,您会看到大多数内联函数只有1-3行。

⚠️ Avoid inlining large functions!

Avoid️ 避免内联大型函数!

⚠️ When using inline functions, you’re not allowed to keep a reference to the functions passed as parameter or pass it to a different function — you’ll get a compiler error saying Illegal usage of inline-parameter.

using️使用内联函数时, 不允许保留对作为参数传递的函数的引用或将其传递给其他函数—会出现编译器错误,指出Illegal usage of inline-parameter 。

So, for example, let’s modify the edit method and the saveToken method. edit method gets another parameter that is then passed to a different function. saveToken uses a dummy variable that gets updated in the new function:

因此,例如,让我们修改edit方法和saveToken方法。 edit方法获取另一个参数,然后将其传递给另一个函数。 saveToken使用一个虚拟变量,该变量在新函数中进行更新:

fun myFunction(importantAction: Int.() -> Unit) {
importantAction(-1)
}inline fun SharedPreferences.edit(
commit: Boolean = false,importantAction: Int.() -> Unit = { },
action: SharedPreferences.Editor.() -> Unit
) {myFunction(importantAction)
...

}
...
fun saveToken(token: String) {
var dummy = 3
preferences.edit(importantAction = { dummy = this}) {
putString(KEY_TOKEN, token)
}
}

We can see that myFunction(importantAction) produces an error:

我们可以看到myFunction(importantAction)产生一个错误:

Here’s how you can solve this, depending on how your function looks like:

根据函数的外观,可以按照以下方法解决此问题:

Case 1: If you have multiple functions as parameters and you only need to keep a reference to one of them, then you can lmdjz it as noinline.

情况1 :如果您有多个函数作为参数,而只需要保留对其中一个的引用,则可以将其标记为noinline 。

By using noinline, the compiler will create a new Function object only for that specific function, but the rest will be inlined.

通过使用noinline ,编译器将仅为该特定函数创建一个新的Function对象,而其余的将被内联。

Our edit function will now be:

现在,我们的edit功能将是:

inline fun SharedPreferences.edit(
commit: Boolean = false,noinline importantAction: Int.() -> Unit = { },
action: SharedPreferences.Editor.() -> Unit
) {
myFunction(importantAction)
...
}

If we check the bytecode, we see that a NEW call appeared:

如果我们检查字节码,我们会看到出现了一个NEW调用:

NEW com/example/inlinefun/PreferencesManager$saveToken$1

In the decompiled code we can see the following (comments mine):

在反编译的代码中,我们可以看到以下内容(我的评论):

Case 2: If your function only has one function as a parameter, just prefer not using inline at all. If you do want to use inline, you’d have to lmdjz your parameter with noinline, but like this you’ll have low performance benefits by inlining the method.

情况2 :如果您的函数只有一个函数作为参数,则只希望根本不使用inline 。 如果确实要使用内联,则必须用noinline标记参数,但是这样,通过内联该方法将降低性能。

To decrease the memory allocations caused by lambda expressions, use the inline keyword! Make sure you apply it to small functions that take a lambda as a parameter. If you need to keep a reference to a lambda or pass it as an argument to another function use the noinline keyword. Start inlining to start saving!

要减少由lambda表达式引起的内存分配,请使用inline关键字! 确保将其应用于以lambda作为参数的 小函数 。 如果您需要保留对lambda的引用或将其作为参数传递给另一个函数,请使用noinline关键字。 开始内联以开始保存!

翻译自: https://medium.com/androiddevelopers/inline-functions-under-the-hood-12ddcc0b3a56

内联函数什么时候展开

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。