簡體   English   中英

C# 中的內聯函數?

[英]Inline functions in C#?

你如何在 C# 中執行“內聯函數”? 我不認為我理解這個概念。 它們像匿名方法嗎? 像 lambda 功能?

注意:答案幾乎完全涉及內聯函數的能力,即“用被調用者的主體替換 function 調用站點的手動或編譯器優化”。 如果您對匿名(又名 lambda)函數感興趣,請參閱@jalf 的回答每個人都在談論的“Lambda”是什么? .

最后,在 .NET 4.5 中,CLR 允許使用MethodImplOptions.AggressiveInlining值提示/建議1 個方法內聯。 它也可以在 Mono 的后備箱中使用(今天已提交)。

// The full attribute usage is in mscorlib.dll,
// so should not need to include extra references
using System.Runtime.CompilerServices; 

...

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MyMethod(...)

1 . 以前在這里使用“力”。 由於有一些反對票,我將嘗試澄清該術語。 正如在評論和文檔中一樣, The method should be inlined if possible.The method should be inlined if possible. 特別是考慮到 Mono(它是開放的),考慮到內聯或更通用的技術限制(例如虛函數),存在一些特定於 Mono 的技術限制。 總的來說,是的,這是對編譯器的一個提示,但我想這就是所要求的。

內聯方法只是一種編譯器優化,其中函數的代碼被滾動到調用者中。

在 C# 中沒有執行此操作的機制,並且在支持它們的語言中應謹慎使用它們——如果您不知道為什么應該在某處使用它們,那么它們就不應該使用。

編輯:為了澄清,有兩個主要原因需要謹慎使用它們:

  1. 在不需要的情況下使用內聯很容易制作大量二進制文件
  2. 從性能的角度來看,編譯器往往比你更清楚什么時候應該內聯

最好不要管它,讓編譯器完成它的工作,然后分析並確定內聯是否是最適合您的解決方案。 當然,有些事情內聯才有意義(尤其是數學運算符),但讓編譯器處理它通常是最佳實踐。

更新:根據konrad.kruczynski 的回答,以下適用於 .NET 版本(包括 4.0)。

您可以使用MethodImplAttribute 類防止方法被內聯...

[MethodImpl(MethodImplOptions.NoInlining)]
void SomeMethod()
{
    // ...
}

...但沒有辦法做相反的事情並強制它內聯。

你混淆了兩個不同的概念。 函數內聯是一種編譯器優化,對語義沒有影響。 無論是否內聯,函數的行為都是相同的。

另一方面,lambda 函數純粹是一個語義概念。 對它們應該如何實現或執行沒有要求,只要它們遵循語言規范中規定的行為。 如果 JIT 編譯器喜歡,它們可以被內聯,如果不喜歡,則不可以。

C# 中沒有 inline 關鍵字,因為它是一種通常可以留給編譯器的優化,尤其是在 JIT 語言中。 JIT 編譯器可以訪問運行時統計信息,這使它能夠比編寫代碼時更有效地決定要內聯的內容。 如果編譯器決定內聯一個函數,那么無論哪種方式你都無能為力。 :)

科迪說得對,但我想提供一個內聯函數是什么的例子。

假設您有以下代碼:

private void OutputItem(string x)
{
    Console.WriteLine(x);

    //maybe encapsulate additional logic to decide 
    // whether to also write the message to Trace or a log file
}

public IList<string> BuildListAndOutput(IEnumerable<string> x)
{  // let's pretend IEnumerable<T>.ToList() doesn't exist for the moment
    IList<string> result = new List<string>();

    foreach(string y in x)
    {
        result.Add(y);
        OutputItem(y);
    }
    return result;
}

編譯器 即時優化器可以選擇更改代碼,以避免在堆棧上重復調用 OutputItem(),這樣就好像您已經編寫了這樣的代碼:

public IList<string> BuildListAndOutput(IEnumerable<string> x)
{
    IList<string> result = new List<string>();

    foreach(string y in x)
    {
        result.Add(y);

        // full OutputItem() implementation is placed here
        Console.WriteLine(y);   
    }

    return result;
}

在這種情況下,我們會說 OutputItem() 函數是內聯的。 請注意,即使 OutputItem() 也從其他地方調用,它也可能會這樣做。

編輯以顯示更可能被內聯的場景。

你是說 C++ 意義上的內聯函數嗎? 其中普通函數的內容會自動內聯復制到調用站點中? 最終效果是在調用函數時實際上沒有發生函數調用。

例子:

inline int Add(int left, int right) { return left + right; }

如果是,則不,沒有與此等效的 C#。

或者你的意思是在另一個函數中聲明的函數? 如果是,則是,C# 通過匿名方法或 lambda 表達式支持這一點。

例子:

static void Example() {
  Func<int,int,int> add = (x,y) => x + y;
  var result = add(4,6);  // 10
}

是的,唯一的區別是它返回一個值。

簡化(不使用表達式):

List<T>.ForEach一個動作,它不期望返回結果。

所以Action<T>委托就足夠了..說:

List<T>.ForEach(param => Console.WriteLine(param));

等同於說:

List<T>.ForEach(delegate(T param) { Console.WriteLine(param); });

不同之處在於參數類型和委托聲明是通過使用推斷的,並且簡單的內聯方法不需要大括號。

然而

List<T>.Where一個函數,期待一個結果。

所以一個Function<T, bool>應該是:

List<T>.Where(param => param.Value == SomeExpectedComparison);

這與:

List<T>.Where(delegate(T param) { return param.Value == SomeExpectedComparison; });

您還可以內聯聲明這些方法並將它們分配給變量 IE:

Action myAction = () => Console.WriteLine("I'm doing something Nifty!");

myAction();

或者

Function<object, string> myFunction = theObject => theObject.ToString();

string myString = myFunction(someObject);

我希望這有幫助。

有時我確實希望強制代碼內聯。

例如,如果我有一個復雜的例程,其中在一個高度迭代的塊中做出了大量決策,並且這些決策導致要執行的類似但略有不同的操作。 例如,考慮一個復雜的(非 DB 驅動的)排序比較器,其中排序算法根據許多不同的不相關標准對元素進行排序,例如如果根據快速語言的語法和語義標准對單詞進行排序,則可能會這樣做識別系統。 我傾向於編寫輔助函數來處理這些操作,以保持源代碼的可讀性和模塊化。

我知道這些輔助函數應該是內聯的,因為如果代碼永遠不需要被人類理解,那么這就是編寫代碼的方式。 我當然想確保在這種情況下沒有函數調用開銷。

聲明“最好不要管這些事情,讓編譯器完成工作......”(Cody Brocious)完全是垃圾。 我已經編寫了 20 年的高性能游戲代碼,但我還沒有遇到一個“足夠聰明”的編譯器,可以知道哪些代碼應該被內聯(函數)。 在 c# 中有一個“內聯”語句會很有用,事實是編譯器沒有它需要的所有信息來確定在沒有“內聯”提示的情況下應該始終內聯或不內聯哪個函數。 當然,如果函數很小(訪問器),那么它可能會自動內聯,但如果它是幾行代碼呢? 廢話,編譯器無法知道,你不能把它留給編譯器來優化代碼(超越算法)。

我知道這個問題是關於 C# 的。 但是,您可以使用 F# 在 .NET 中編寫內聯函數。 請參閱: 在 F# 中使用 `inline`

不,C# 中沒有這樣的構造,但是 .NET JIT 編譯器可以決定在 JIT 時間執行內聯函數調用。 但我實際上不知道它是否真的在做這樣的優化。
(我認為它應該:-))

如果您的程序集將被生成,您可能需要查看 TargetedPatchingOptOut。 這將幫助 ngen 決定是否內聯方法。 MSDN參考

盡管如此,它仍然只是一個聲明性的優化提示,而不是一個命令式命令。

Lambda 表達式是內聯函數! 我認為,C# 沒有像 inline 之類的額外屬性!

C# 不支持像 python 這樣的動態語言那樣的內聯方法(或函數)。 但是,匿名方法和 lambdas 可用於類似目的,包括當您需要訪問包含方法中的變量時,如下例所示。

static void Main(string[] args)
{
    int a = 1;

    Action inline = () => a++;
    inline();
    //here a = 2
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM