Solving One of the Biggest Array Issues in C#

Поділитися
Вставка
  • Опубліковано 25 чер 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

КОМЕНТАРІ • 81

  • @MZZenyl
    @MZZenyl 10 місяців тому +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 10 місяців тому +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 10 місяців тому +113

    Please do a video on ConfigureAwait. Thanks

    • @MRender32
      @MRender32 10 місяців тому +8

      yeah especially since .NET 8 got a ConfigureAwaitOptions enum overload

    • @peryvindhavelsrud590
      @peryvindhavelsrud590 10 місяців тому +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 10 місяців тому +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 10 місяців тому +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 10 місяців тому +2

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

  • @justwatching6118
    @justwatching6118 10 місяців тому +10

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

  • @sonpham7617
    @sonpham7617 10 місяців тому +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 10 місяців тому

      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 10 місяців тому

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

    • @Rob_III
      @Rob_III 10 місяців тому +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 10 місяців тому

      @@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.

  • @peryvindhavelsrud590
    @peryvindhavelsrud590 10 місяців тому +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.

  • @F1nalspace
    @F1nalspace 10 місяців тому +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 10 місяців тому +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 10 місяців тому +2

      Dangling pointers are so much fun

  • @user-tk2jy8xr8b
    @user-tk2jy8xr8b 10 місяців тому +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 9 місяців тому

      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 9 місяців тому

      @@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

  • @Stdvwr
    @Stdvwr 10 місяців тому +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?

  • @the-niker
    @the-niker 10 місяців тому

    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 10 місяців тому +3

    I just noticed the background image of your logging course 😂

  • @kpamafrederic6558
    @kpamafrederic6558 10 місяців тому +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 10 місяців тому

    It was very helpful information. Thank you so much.

  • @phoenixwyllow
    @phoenixwyllow 10 місяців тому +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 10 місяців тому +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.

  • @fehmidenguir6278
    @fehmidenguir6278 10 місяців тому

    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  10 місяців тому

      It is built in out of the box in Rider

  • @inzyster
    @inzyster 10 місяців тому

    Interesting stuff, definitely useful for some of my projects.

  • @MRender32
    @MRender32 10 місяців тому +1

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

  • @YuraSuper2048
    @YuraSuper2048 10 місяців тому +1

    Please make a video about unsafe it seems very fun

  • @feefifofum6383
    @feefifofum6383 10 місяців тому +1

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

  • @JustinLampe
    @JustinLampe 10 місяців тому +1

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

  • @guiorgy
    @guiorgy 10 місяців тому

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

    • @nickchapsas
      @nickchapsas  10 місяців тому +3

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

  • @nanvlad
    @nanvlad 10 місяців тому +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 10 місяців тому +2

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

  • @justwatching6118
    @justwatching6118 10 місяців тому +1

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

  • @Eduardoo
    @Eduardoo 10 місяців тому

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

  • @alexanderkvenvolden4067
    @alexanderkvenvolden4067 10 місяців тому

    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 10 місяців тому

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

  • @lordshoe
    @lordshoe 10 місяців тому +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 10 місяців тому +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  10 місяців тому +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 10 місяців тому +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.

  • @Lubgi3
    @Lubgi3 10 місяців тому

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

    • @nickchapsas
      @nickchapsas  10 місяців тому +1

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

  • @leandroteles7857
    @leandroteles7857 10 місяців тому +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)

    • @manilladrift
      @manilladrift 10 місяців тому +2

      Thanks for explaining!

  • @jonasgranlund4427
    @jonasgranlund4427 10 місяців тому

    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  10 місяців тому +3

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

  • @Dustyy01
    @Dustyy01 10 місяців тому +4

    More unsafe stuff🎉

  • @urbanelemental3308
    @urbanelemental3308 10 місяців тому

    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.

  • @neociber24
    @neociber24 10 місяців тому

    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

  • @kaiserbergin
    @kaiserbergin 10 місяців тому

    feels like ReadOnlyArray - but I'll take it!

  • @AlFasGD
    @AlFasGD 10 місяців тому +1

    And as always, keep coding unsafely

  • @Crozz22
    @Crozz22 10 місяців тому +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  10 місяців тому +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 10 місяців тому +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 10 місяців тому +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?

  • @SeanAlunni
    @SeanAlunni 10 місяців тому

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

  • @figloalds
    @figloalds 10 місяців тому +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

  • @paarma1752
    @paarma1752 10 місяців тому

    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.

  • @EddieDemon
    @EddieDemon 10 місяців тому

    Haha, “tl;dw” 😂

  • @ryanzwe
    @ryanzwe 10 місяців тому

    :o

  • @user-zk5ym9ut1j
    @user-zk5ym9ut1j 10 місяців тому +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 10 місяців тому +1

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

  • @Bliss467
    @Bliss467 10 місяців тому

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

  • @anomalii7720
    @anomalii7720 10 місяців тому

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

  • @dmitrykim3096
    @dmitrykim3096 10 місяців тому

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

  • @robbielowe2516
    @robbielowe2516 10 місяців тому

    🌸 Promo'SM

  • @Mitakbacktrack
    @Mitakbacktrack 10 місяців тому

    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 10 місяців тому +2

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

  • @mrwensveen
    @mrwensveen 10 місяців тому

    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 10 місяців тому +1

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