简体   繁体   中英

Why .NET uses reference type inside struct?

I am working on Blazor project, and I am using Virtualization component. I don't have any issue, but just trying to understand one thing.

I found that in Virtualization component there is ItemsProvider which returns ItemsProviderResult<TItem> . It is Value type, but has a property Items which is collection of reference types.

And here are my questions:

  • If we lose a scope of ItemsProviderResult<TItem> will reference to Items be left in memory or it will be removed?
  • How could I benchmark Struct vs Class performance differences in this particular case?
  • What do you think, why Blazor has done so?

I have googled general information about Value and Reference types. I found information about that struct is recommended to be always immutable. But I haven't found an answer for question what happens with Reference type object if struct loses scope.

UPD1:

I understand that the collection of reference type and any other reference type property will be released from memory by GC, but will it be at the same time when struct lose scope or it will be staying in memory until GC will remove it?

If we lose a scope of ItemsProviderResult<TItem> will reference to Items be left in memory or it will be removed?

In C#, if something goes out of scope it will eventually be released from memory.

So for Items , it will become eligible for garbage collection when there are no more references to the struct that contains it or Items itself. Items will remain in memory until released by the garbage collector.

How could I benchmark Struct vs Class performance differences in this particular case?

You can make use of the Benchmark.NET library for testing methods that perform the desired operations using structs/classes.

What do you think, why Blazor has done so?

Items will be a collection of whatever TItem is.

If TItem is a reference type, the you will have a collection of reference types.

If TItem is a value type, the you will have a collection of value types.

Value types will either be stored in the stack, or on the heap as part of an reference object [*]. Because of this value types are typically cheaper. If they are stored on the stack they will be handled automatically by the stack. If they are part on an object they will be collected as part of that object, the garbage collector does not need to do any extra work to keep track of it.

In contrast, reference types will always be stored on the heap. They will also need some extra memory for the GC to use for tracking and marking objects.

In some sense, value types is just a way to avoid listing multiple parameters. They might as well not exist at all as far as the garbage collector is concerned. A struct holding a reference going out of scope will be handled exactly the same as the reference itself going out of scope. Ie the GC will collect the referenced object when it gets around to it. Assuming there are no other references.

In general, value types are preferred for small, immutable, types. While reference types are preferred for larger types, or if mutation of the value is needed. But whenever you think about performance, consider :

There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

Yet we should not pass up our opportunities in that critical 3%.

Ie it probably does not matter unless you have many thousands of objects.

Library authors need to be more careful, since they do not know how their library is used. And they cannot really change their mind if they realize they made a incorrect mistake, since that might break the code for thousands of users.

[*] Technically, stack/heap are implementation details.

As for the How could I benchmark Struct vs Class performance differences in this particular case? .

Benchmark methodology is below, idea is to simulate Virtualization scenario.

[MemoryDiagnoser]
public class GridItemsProviderResultTest
{
    record class Model( string Name, string Description );

    int dataCount = 5000;
    int requestSize = 20;
    ICollection<Model> itemsList = new List<Model>();
    
    public DataSourceResultTest()
    {
        for( int i = 0; i < dataCount; i++ )
        {
            itemsList.Add( new Model( i + " name " + i, i + " description " + i ) );
        }
    }

    [Fact]
    [Benchmark]
    public void VirtualizedRows()
    {
        for( int i = 0; i < dataCount; i += requestSize )
        {
            GridItemsProviderRequest<Model> request = new( i, requestSize, null, false, default );
            IQueryable<Model> data = itemsList.AsQueryable()
                .Skip( request.StartIndex )
                .Take( request.Count.Value );
            GridItemsProviderResult<Model> items = GridItemsProviderResult.From( data.ToArray(), data.Count() );

            Assert.True( items.Data.Count == requestSize );
        }
    }
}

Benchmark results.

GridItemsProviderResult is struct, TGridItem is class

Method Mean Error StdDev Gen0 Gen1 Allocated
VirtualizedRows 74.71 ms 0.181 ms 0.160 ms 714.2857 285.7143 5.96 MB

GridItemsProviderResult is class, TGridItem is class

Method Mean Error StdDev Gen0 Gen1 Allocated
VirtualizedRows 74.63 ms 0.221 ms 0.206 ms 714.2857 285.7143 5.97 MB

GridItemsProviderResult is struct, TGridItem is struct

Method Mean Error StdDev Gen0 Gen1 Allocated
VirtualizedRows 71.90 ms 0.324 ms 0.303 ms 714.2857 285.7143 5.95 MB

GridItemsProviderResult is class, TGridItem is struct

Method Mean Error StdDev Gen0 Gen1 Allocated
VirtualizedRows 71.83 ms 0.400 ms 0.354 ms 714.2857 285.7143 5.96 MB

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM