C# - 传递参数以避免 GC 卡顿的正确方法?

问题描述 投票:0回答:1

(我发现了一些相关的问题,但它们并不完全相同,也不是十年前的问题。)

那么,C# 中的参数是作为值还是引用传递的。

说我愿意,

static void main()
{
    string name = "hello world";
    Console.WriteLine(name);
    testfunc(name);
    Console.WriteLine(name);
}

void testfunc(string name)
{
    name = "stackoverflow";
    Console.WriteLine(name);
}

#Output
=> hello world
=> stackoverflow
=> hello world

因此,此参数作为值传递,不再与原始变量(在内存中)关联。我猜这意味着变量被复制到内存中的新位置然后传递(这涉及分配、复制,然后传递给函数)。

然后我们这样做:

static void main()
{
    string name = "hello world";
    Console.WriteLine(name);
    testfunc(ref name);
    Console.WriteLine(name);
}

void testfunc(ref string name)
{
    name = "stackoverflow";
    Console.WriteLine(name);
}

#Output
=> hello world
=> stackoverflow
=> stackoverflow

因此,我猜原始变量引用/指针被传递到函数中,并且没有发生新内存/数据的分配或复制。

所以,我在想第一种方法是否有一些开销(对于数据 >= 10MB 肯定有)。但第二种方法是不安全的,因为它可能会损坏原始数据。

那么,我们应该做什么或者可以进行什么样的优化来克服第一种方法的开销(如果有的话)?对于从函数返回的值也同样有效。

**我的用例:**我正在统一开发一款游戏。我必须创造一个世界。为此,我创建了 3D 噪声图(非常大,而且针对湿度、文明、地形等也有多个)。它们总共占用大约 70-80 MB 的内存。现在,我将它们全部传递给一个函数,将它们组合起来,并生成最终的世界。因此,如果 100 MB 的数据被复制到内存中(就像上面的第一种方法一样),那么我认为对于具有 <= 4GB of ram. As for the data returned from a function, let's say I have a save file of 25 MB (some games like RimWorld have such large save files). I call the

loadSaveFile()
功能的机器来说,这不会很好(它读取文件,将其转换为
SaveClass
并将其存储在变量
saveData
中,然后返回)。这个
saveData
变量的大小应该约为 25-28 MB。所以,如果返回的数据也是重复的,那么对于这么大的数据来说是不好的。

我知道重复的数据最终会被 GC 销毁,但是当 GC 销毁它时,这会影响该特定帧的帧速率。这可以通过使用增量 GC 来解决,但仍然会导致内存膨胀(因为创建和销毁重复数据的那几秒钟会影响机器的整体性能)。

所以,最后一个问题是: 将大数据作为参数传递给函数并接收大数据作为返回值的正确、最佳和最实用的方法是什么?

c# unity-game-engine garbage-collection parameter-passing game-development
1个回答
0
投票
In C#, parameters are passed by value by default. This means that when you pass a parameter to a method, a copy of the value (not the reference) is passed to the method, and any changes made to the parameter within the method do not affect the original variable that was passed. This is the behavior you observed in your first code example.

In your first code example, you have:

csharp
Copy code
static void Main()
{
    string name = "hello world";
    Console.WriteLine(name);
    testfunc(name);
    Console.WriteLine(name);
}

void testfunc(string name)
{
    name = "stackoverflow";
    Console.WriteLine(name);
}
The output shows that changes made to the name variable inside the testfunc method do not affect the original name variable in the Main method. This is because the parameter name in testfunc is a separate copy of the original value.

In your second code example, you use the ref keyword to pass the parameter by reference:

    static void Main()
    {
        string name = "hello world";
        Console.WriteLine(name);
        testfunc(ref name);
        Console.WriteLine(name);
    }

void testfunc(ref string name)
{
    name = "stackoverflow";
    Console.WriteLine(name);
}
When you use ref, you are passing a reference to the original variable, and any changes made to the parameter name inside the testfunc method will affect the original name variable in the Main method. This is why you see the modified value in the output.

Regarding your concern about GC (Garbage Collection) stutters, the way you pass parameters (by value or by reference) typically does not have a significant impact on rubbish collection. Rubbish collection is primarily concerned with reclaiming memory that is no longer in use, and it occurs independently of how method parameters are passed.

If you are concerned about minimizing GC pauses in your application, you should focus on other factors, such as managing object lifetimes, avoiding unnecessary object allocations, and using appropriate data structures and patterns to reduce memory pressure. Pass-by-value vs. pass-by-reference is not a primary factor in managing GC behaviour.
© www.soinside.com 2019 - 2024. All rights reserved.