Solving One of the Biggest Array Issues in C#

Sdílet
Vložit
  • čas přidán 25. 06. 2023
  • Check out my new course From Zero to Hero: Logging in .NET and use code LOG20 at checkout for 20% off: bit.ly/loggingdotnet valid for the first 400 purchases!
    Become a Patreon and get source code access: / nickchapsas
    Hello, everybody, I'm Nick, and in this video, I will show you how you can solve the biggest issue with Immutable Array's performance in C# and .NET.
    Workshops: bit.ly/nickworkshops
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasGitHub
    Follow me on Twitter: bit.ly/ChapsasTwitter
    Connect on LinkedIn: bit.ly/ChapsasLinkedIn
    Keep coding merch: keepcoding.shop
    #csharp #dotnet

Komentáře • 81

  • @MZZenyl
    @MZZenyl Před 10 měsíci +34

    Unsafe (the class), unsafe (the keyword), P/Invoke, and other topics relating to marshalling and manual memory management, is always good fun.
    For most people, using these is rarely if ever necessary (especially now that we have the Memory and Span structs), but they're a great learning opportunity that can lead to a much deeper understanding of what actually makes .NET tick.

    • @davidmartensson273
      @davidmartensson273 Před 10 měsíci +1

      Yes, same with reflection, you can cause some very interesting errors :P
      My favorit was "if(a == 0) ..." causing null reference exception. And yes it was the comparisson and no there was not overloaded operators, just comparing and int variable with zero triggered null ref.
      And a was an argument to the function "... MyFunc(int a) { ..."
      You cannot do that without some serious deep diving into the dark corners of C#.

  • @cdarrigo
    @cdarrigo Před 10 měsíci +113

    Please do a video on ConfigureAwait. Thanks

    • @MRender32
      @MRender32 Před 10 měsíci +8

      yeah especially since .NET 8 got a ConfigureAwaitOptions enum overload

    • @peryvindhavelsrud590
      @peryvindhavelsrud590 Před 10 měsíci +2

      Hopefully ConfigureAwait is soon to be deprecated except for the rare circumstances where you need to use ConfigureAwait(true); which, if my understanding is correct, only applies for UI thread async operations.

    • @axelbreekweg
      @axelbreekweg Před 10 měsíci +2

      @@peryvindhavelsrud590ConfigureAwait(true) is the default. So if you were to omit this, it’s still always configured this way. And yes, for technologies like WinForms and WPF this is important since it has a UI thread. You need to make sure the continuation task is taken by the UI thread. In these environments ConfigureAwait is relevant, you could use ConfigureAwait(false) if you wish that any other thread than the UI thread can continue the task.

    • @peryvindhavelsrud590
      @peryvindhavelsrud590 Před 10 měsíci +1

      @@axelbreekweg my point being that it should be possible to alter the default to ConfigureAwait(false) per project, so my libraries by default have the desired behaviour so i don't have to write it everywhere.

  • @codeyhuntting8830
    @codeyhuntting8830 Před 10 měsíci +2

    Love the polite frankness at 0:09 - "If you've never used immutable array, then... I'm a bit about that" XD

  • @sonpham7617
    @sonpham7617 Před 10 měsíci +47

    It's interesting with ImmutableArray, but it causes potential issues if somebody without these knowledge or by mistake use add or remove method, which creates a whole new array.
    I think IReadOnlyList and IReadOnlyCollection would be better which dont provide add or remove method.

    • @urbanelemental3308
      @urbanelemental3308 Před 10 měsíci

      I'm in agreement here. I know there can be some performance degradation when accessing an interface and not just the struct, but in this case, I would definitely make a new struct like ReadOnlyArray that makes it clear that the underlying values can change (by the owner) but cannot be changed by the consumer.

    • @anm3037
      @anm3037 Před 10 měsíci

      I actually never understand the reason for providing all those methods to modify IMMUTABLE Array. IReadOnlyCollection is far better

    • @Rob_III
      @Rob_III Před 10 měsíci +1

      @@anm3037 An immutable structure is useful for when multiple threads are accessing the items in it for example. You have the guarantee that nobody is modifying the array while a bunch of threads are modifying it. But that doesn't mean you may don't want to modify the array; so there are methods that do that for you by making a copy of the array and meanwhile everything keeps working nicely and it's up to you to decide if/when you want to use the new ("modified") array.

    • @anm3037
      @anm3037 Před 10 měsíci

      @@Rob_III I disagree with that. Immutable array should not expose such methods. And unfortunately, there is no other contiguous memory that behaves like ReadOnlyCollection.

  • @justwatching6118
    @justwatching6118 Před 10 měsíci +10

    OFC.. I would really like that you create a (possible longer) video on unsafe :D

  • @F1nalspace
    @F1nalspace Před 10 měsíci +16

    I have encounter that issue several times and always wanted a immutable array that just point to the backing array, so really thanks for sharing that ;-)
    I didn´t knew Unsafe.As() either... i really need to look into this, because i like unsafe stuff ^^

    • @FireDragon91245
      @FireDragon91245 Před 10 měsíci +6

      if you like unsafe i have a neat fun fact you can actualy do
      unsafe class GenericPointer where T : unmanaged
      {
      public T* Ptr;
      }
      its posible to make generic pointers

    • @neociber24
      @neociber24 Před 10 měsíci +2

      Dangling pointers are so much fun

  • @peryvindhavelsrud590
    @peryvindhavelsrud590 Před 10 měsíci +5

    I normally return Read-only Memory or ReadOnlySpan to handle these types. The fact that I'm able to re-retrieve a non Read-only version is a bit sus. The unsafe option is also a possibility for avoiding allocations, not sure if i see any real difference except for that the new Marshalling methods make it even easier to get the mutable array back.

  • @user-tk2jy8xr8b
    @user-tk2jy8xr8b Před 10 měsíci +12

    Most of the time, IReadOnlyList is the right type for the right job, not ImmutableArray.
    Btw, several years ago we rewrote some pieces of the application to use Immutable* types. Later, after profiling, we rewrote them back to Dictionary/HashSet/List+IReadOnly* because of poor access time (log vs const) and memory cost (a tree obviously consumes more memory than a hash table, plus worse localization leads to CPU cache misses, but that's more in theory than in practice), both of which were important for the app.
    Immutable arrays are an exception here, because they are optimal by both space and time (even better than IReadOnlyList as interface calls are heavier, with the v-table linear search unless I'm mistaking about that). However, ImmutableArray has one functional downside when compared to IReadOnlyList: it implements ICollection and IList which violates the Liskov substitution principle, so good luck calling `void Add(T)`

    • @fluffydoggo
      @fluffydoggo Před 9 měsíci

      Keep in mind ICollection and IList have a IsReadOnly property, so that can be checked for if you really want to

    • @user-tk2jy8xr8b
      @user-tk2jy8xr8b Před 9 měsíci

      @@fluffydoggo yes, it's like using one interface for everything in your program, just use IsBehavior1, IsBehavior2 and so on to decide, what methods are supported by a particular instance

  • @the-niker
    @the-niker Před 10 měsíci

    Good stuff I can see it being used in cases where a method creates and fills an array dynamically but returns immutable version of it in the end and discards the original for zero additional cost.

  • @zwatotem
    @zwatotem Před 10 měsíci +3

    I just noticed the background image of your logging course 😂

  • @Stdvwr
    @Stdvwr Před 10 měsíci +1

    Why would you create an array just to cast it to immutable array? Why not ToImmutableArray directly from the enumerable? What about ImmutableArray.Builder?

  • @phoenixwyllow
    @phoenixwyllow Před 10 měsíci +2

    I believe quite a few libraries use the ToImmutableArray stuff to generate things once and some use it for exactly the purpose of ensuring the internal array doesn't mutate.
    I would love to have a clean immutable or readonly implementation specifically for initalizing (avoiding the creation of the first mutable array) so we don't have to resort to unsafe or Unsafe calls...
    I suppose in some cases it may just be worth forcing a breaking change in favour of ReadOnlyList, but sometimes that's just not possible or simply the wrong approach...

  • @cn-ml
    @cn-ml Před 10 měsíci +3

    Honestly, i think the problem is a different one if your need to use this. If your application requieres you to access the backing field of the immutable collection, you shouldnt have used an immutable collection in the first place. Also, if your application requieres a fast immutable collection access without reallocation, i think you should make that clear using the api interface, using the AsReadOnly methods.

  • @kpamafrederic6558
    @kpamafrederic6558 Před 10 měsíci +3

    All right. I'm definitely gonna need that video on Unsafe. I failed at converting a ReadOnlySpan to ReadOnlyMemory without new allocation 2 times now

  • @kingumosama
    @kingumosama Před 10 měsíci

    It was very helpful information. Thank you so much.

  • @MRender32
    @MRender32 Před 10 měsíci +1

    nice! i thought this would be about collection literals, pleasantly surprised

  • @inzyster
    @inzyster Před 10 měsíci

    Interesting stuff, definitely useful for some of my projects.

  • @fehmidenguir6278
    @fehmidenguir6278 Před 10 měsíci

    Hello Nick, thanks for the video. I wanted to ask how you can access the method implementation of the dotnet framework through your IDE ? Is it a plugin or it's supported out of the box. Also is it supported in Visual Studio

    • @nickchapsas
      @nickchapsas  Před 10 měsíci

      It is built in out of the box in Rider

  • @feefifofum6383
    @feefifofum6383 Před 10 měsíci +1

    So is it quicker to loop over immutable arrays compared to normal arrays?

  • @guiorgy
    @guiorgy Před 10 měsíci

    Isn't it simpler to just use a ReadOnlySpan(T[])? Does the ImmutableArray have any benefits that ReadOnlySpan does not?

    • @nickchapsas
      @nickchapsas  Před 10 měsíci +3

      ReadOnlySpan is a ref struct so it can only be stack allocated which limits where it can be used

  • @justwatching6118
    @justwatching6118 Před 10 měsíci +1

    Yeah.. I've seen a LOT of times people over abusing lists (use them for every type of collection)..

  • @nanvlad
    @nanvlad Před 10 měsíci +2

    What if I use users[0] = myVar? Does it create a new array for that or just replace first item with a new value?

    • @iamprovidence-xj5wf
      @iamprovidence-xj5wf Před 10 měsíci +2

      It does not have setter, so such operation is just not possible

  • @JustinLampe
    @JustinLampe Před 10 měsíci +1

    A new Rider EAP with AI integration. We need a video for this!

  • @alexanderkvenvolden4067
    @alexanderkvenvolden4067 Před 10 měsíci

    Does this do the same thing as using reflection to call the internal constructor? I suppose the advantage to that is not relying on non-supported means which Microsoft could break without notice.

    • @phizc
      @phizc Před 10 měsíci

      Another bonus is that it avoids boxing the result. Using reflection would return the ImmutableArray as an Object.

  • @YuraSuper2048
    @YuraSuper2048 Před 10 měsíci +1

    Please make a video about unsafe it seems very fun

  • @Lubgi3
    @Lubgi3 Před 10 měsíci

    Can’t we return ReadOnlySpan for that purpose? Is there any difference?

    • @nickchapsas
      @nickchapsas  Před 10 měsíci +1

      ReadOnlySpan is a ref struct so it can only be stack allocated which limits where it can be used

  • @lordshoe
    @lordshoe Před 10 měsíci +2

    So, how is it actually any more immutable than before if you can just get the backing array anyways? Or is it mostly just for the convenience it offers?
    I've never used ImmutableArray, but what benefits does it provide over exposing something like an IReadOnlyCollection?

    • @protox4
      @protox4 Před 10 měsíci +2

      It's faster. Interfaces make iterating much slower.
      Those are unsafe methods, so you should not be using them in general code. They are safe if you use them correctly.

    • @nickchapsas
      @nickchapsas  Před 10 měsíci +8

      It's the safety net it provides. You could always do something like user reflection and hack your way around the problem but that would have been inefficient. ReadOnly data structures and Immutable data structures are very different in C#. Maybe they deserve a video of their own

    • @yjagota
      @yjagota Před 10 měsíci +7

      @@nickchapsas I had the same question. A video on benefits of ImmutableArray over List or IReadOnlyList will be great. And thanks for all the great videos Nick. Keep up the good work.

  • @jonasgranlund4427
    @jonasgranlund4427 Před 10 měsíci

    Since It is so many comment regarding the IReadonlyList... instead of an ImmutableArray I have a feeling we will get a post about those in a near time :)

    • @nickchapsas
      @nickchapsas  Před 10 měsíci +3

      I think I'll do a video about all these types because people seem to largely misunderstasnd them and misuse them

  • @neociber24
    @neociber24 Před 10 měsíci

    I crashed programs so many times using unsafe that's how you know is fun 😂
    I feel weird those marshal methods are not marked as unsafe

  • @leandroteles7857
    @leandroteles7857 Před 10 měsíci +9

    It's worth noting that this only works because ImmutableArray is a struct. (An object reference and "a struct with an object reference" have the same memory representation)

  • @urbanelemental3308
    @urbanelemental3308 Před 10 měsíci

    I feel like it should be a different struct/class that is a wrapper around the array like a ReadOnlyArray or something like that. Allowing 'hacks' like this seems to break the intention.

  • @Eduardoo
    @Eduardoo Před 10 měsíci

    I'm surprised that frozen collections weren't mentioned in the video

  • @SeanAlunni
    @SeanAlunni Před 10 měsíci

    If the "ImmutableCollectionsMarshal" actually calls the internal constructor, how does it manage to not allocate any byte?

  • @Dustyy01
    @Dustyy01 Před 10 měsíci +4

    More unsafe stuff🎉

  • @Crozz22
    @Crozz22 Před 10 měsíci +1

    I don't understand the point of this, you can just pass around an IReadOnlyList instead. Arrays implement IReadOnlyList. Considering you have to be careful to not change the backing array/list anyway with both approaches.

    • @nickchapsas
      @nickchapsas  Před 10 měsíci +1

      Intent is the point. Why would you use a ReadOnlyLIST to represent an array that cannot be mutated and why would you give up on the ability to generate new immutable versions of that data structure by not using ImmutableArrays?

    • @chris-pee
      @chris-pee Před 10 měsíci +4

      @@nickchapsas Just because .NET collection interfaces make no sense whatsoever, doesn't mean Immutable represents the intent better (I don't think it does).

  • @Palladin007
    @Palladin007 Před 10 měsíci +1

    Honestly, what is the point of the ImmutableArray? Why not just IReadOnlyList?
    Well, you can cast back, but there is also a way to manipulate the ImmutableArray.
    Or what speaks against ReadOnlyMemory?

  • @EddieDemon
    @EddieDemon Před 10 měsíci

    Haha, “tl;dw” 😂

  • @kaiserbergin
    @kaiserbergin Před 10 měsíci

    feels like ReadOnlyArray - but I'll take it!

  • @paarma1752
    @paarma1752 Před 10 měsíci

    I have found that more often than not the exact requirement is not to map the dto from a single object, but actually from multiple. More often than not it's best to keep the dtos as the API for your domain logic (your domain logic digests a dto and then outputs another one). And this also means that your dtos should be part of your domain.

  • @figloalds
    @figloalds Před 10 měsíci +2

    I don't subscribe to this, to me this is like an extra pair of wheels on the bike of programmers who are bad
    And if I'm saying "ToImmutableArray" and this makes a whole copy, then it's not very different from handing back a modifiable copy array which the user function may need to alter anyways in it's own processing
    And besides, almost everything in OOP is references and I really doubt the "immutable" array makes the underlying objects any immutable, it's still shared state anyways, still requires common sense from the programmers writing the lib and user code

  • @AlFasGD
    @AlFasGD Před 10 měsíci +1

    And as always, keep coding unsafely

  • @ryanzwe
    @ryanzwe Před 10 měsíci

    :o

  • @user-zk5ym9ut1j
    @user-zk5ym9ut1j Před 10 měsíci +1

    Never seen any practical application of ImmutableArray for 7 years, a bit of HashSets here, a couple Queues there, but ImmutableArray is a bit confusing collection

  • @FireDragon91245
    @FireDragon91245 Před 10 měsíci +1

    the only valid array type is T* where T : unmanaged

  • @Bliss467
    @Bliss467 Před 10 měsíci

    Why do we use properties with public get set instead of just a public field? It seems like a completely superfluous wrapper

  • @dmitrykim3096
    @dmitrykim3096 Před 10 měsíci

    I think you had to choose c++, you spend a lot of time on a lower level optimizations and hacks with C#.

  • @robbielowe2516
    @robbielowe2516 Před 9 měsíci

    🌸 Promo'SM

  • @anomalii7720
    @anomalii7720 Před 10 měsíci

    So you have an immutableArray, you copy into a mutable Array and you have the same problem.

  • @Mitakbacktrack
    @Mitakbacktrack Před 10 měsíci

    I think we can also use Array.AsReadOnly(_users). It uses 24 bytes for 10,000 iterations. It's slower - 2.4 ns vs. 0.0082 ns on the Smart() method.

  • @dukefleed9525
    @dukefleed9525 Před 10 měsíci +2

    ImmutableCollectionMarshal.AsArray looks the best way ever to shoot yourself in the foot.

  • @mrwensveen
    @mrwensveen Před 10 měsíci

    My method isn't quite as fast as with Unsafe or ImmutableCollectionsMarshal trickery, but it is quite fast compared to AsImmutable: ImmutableArray.Empty.AddRange(_users!);
    About a 1000 times as fast and doesn't allocate memory (speed is constant so 100 or 10_000 items is similar). And it's totally safe! I swear!

  • @nataliasikorka4825
    @nataliasikorka4825 Před 10 měsíci +1

    I have never encountered this issue, because IReadOnlyList exists :)