The fastest way to iterate a List in C# is NOT what you think

Поділитися
Вставка
  • Опубліковано 14 вер 2022
  • Check out my courses: dometrain.com
    Become a Patreon and get source code access: / nickchapsas
    Hello everybody I'm Nick and in this video I will show you all the way you can iterate a List in C# and then show you what is by far the fastest and most memory efficient way. You might have guessed where this is going :)
    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

КОМЕНТАРІ • 406

  • @ooples
    @ooples Рік тому +9

    I love the humor of using 80085 as your seed and I'm not sure if anyone else caught this old calculator dirty humor

  • @capsey_
    @capsey_ Рік тому +123

    Nick should get a shirt with "I Stan the Span" printed on it

    • @nickchapsas
      @nickchapsas  Рік тому +31

      Coming on a t-shirt near you twitter.com/nickchapsas/status/1523025560774987777

    • @joshstather3543
      @joshstather3543 Рік тому +9

      @@nickchapsas Coming on a t-shirt??? 😳😳

    • @foamtoaster9742
      @foamtoaster9742 Рік тому +3

      I would buy that

    • @Zatrit
      @Zatrit Рік тому +1

      WriteLine'd on it*

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

    .ForEach is not a LINQ method it is actually defined on List

  • @FunWithBits
    @FunWithBits Рік тому +6

    I like how this channel gives a clear statement on what will be answered in a moment. Example: (1) At 1:12 Nick says, "I'm going to put all the ways to iterate over a list here" (2) This allows the user to pause the video and try and think of ways. (3) Then click play and view the 7 different ways. Its great that something like "Feel free to pause the video and try...." as most viewers know this and would not do it anyway. Side note - I was able to only think of three ways. (for loop, and forearch loop, ToArray().Select(x=>x) )

  • @Zindawg02
    @Zindawg02 Рік тому +8

    I've been a C# software engineer for a few years now and until this video I've never heard of a Span (outside the context of html lol). Going to be looking that up, great vid!

  • @user-zh3el2yr6h
    @user-zh3el2yr6h Рік тому +18

    Nick is a legend. Integrating "easter eggs" like 69, 1337 and 80085 always makes me smile

  • @ristopaasivirta9770
    @ristopaasivirta9770 Рік тому +104

    Great benchmark.
    Good that you explained that the parallel versions are most likely faster when you actually do work inside the iterations.
    It always distracts me when you say "half the speed" when you mean "half the time" (ie. double the speed).
    I know it might be a language thingy, but it is really confusing at times.

    • @nickchapsas
      @nickchapsas  Рік тому +73

      Oh damn you are right. I was thinking it in my head in Greek. In English it doesn't really make sense.

    • @ZeroSleap
      @ZeroSleap Рік тому +8

      @@nickchapsas Oh wait,so you are actually Greek huh?

    • @nickchapsas
      @nickchapsas  Рік тому +13

      @@ZeroSleap Yeap

    • @unskeptable
      @unskeptable Рік тому +3

      Actually I'm not sure we say that in Greek either 🤔 Haha very confused

    • @LordErnie
      @LordErnie Рік тому +30

      He a bit geeky, he a bit Greeky

  • @urbanguest
    @urbanguest Рік тому +7

    Your videos are the best and I have learned so much from watching your videos. Keep up the fantastic work! I've been coding for over 30years and I'm still learning new tricks, thanks!

  • @GarethDoherty1985
    @GarethDoherty1985 Рік тому +7

    This was a great video. I love your little deep dives into the C# language.

  • @Tal__Shachar
    @Tal__Shachar Рік тому +24

    Can't express enough how amazing and educational your videos are. Keep doing what you do!!

  • @spacetravelnerd6058
    @spacetravelnerd6058 Рік тому

    Great video! I certainly could have used this on previous projects but will make sure it gives me the benefits I need for my current ones.

  • @sergiom.954
    @sergiom.954 Рік тому +1

    That final console output is really good resume to keep in mind the use of iterations in c#. Very useful 👏👏

  • @LoKSET
    @LoKSET Рік тому +9

    A nice extension method is in order :D
    public static void FastForEach(this List source, Action action)
    {
    var span = CollectionsMarshal.AsSpan(source);
    foreach (var t in span)
    {
    action(t);
    }
    }

  • @itaylorm
    @itaylorm 3 місяці тому

    Very detailed and helpful showing you the results of the typical options Andrew ones I had not seen before

  • @IAmFeO2x
    @IAmFeO2x Рік тому +43

    Great video as always! I also did some collection benchmarks back in April 2022, and i found something different than your benchmarks: for loops were considerably faster than foreach loops. The problem in your benchmark code might be that you access the private field instead of using a variable. foreach will automatically inline the field access to a variable, but for does not do that. In my benchmarks, foreach loops on List were 50% slower than for loops.

    • @nickchapsas
      @nickchapsas  Рік тому +47

      It looks like this got optimized in .NET 7

    • @MaxReble
      @MaxReble Рік тому +3

      Yep, made similar benchmark tests and in dotnet 6 for 100k iterations and foreach took 75 us, for took 37us and Span Foreach 24us. Nice to see that dotnet 7 has many hidden performance boosts!

    • @IAmFeO2x
      @IAmFeO2x Рік тому +2

      @@MaxReble Yep, can confirm, too: I reran my tests with .NET 7 RC1 and for and foreach loops are now nearly identitcal in speed. Still nearly twice as slow as iterating over arrays, spans, or ImmutableArray.

    • @aurinator
      @aurinator Рік тому +1

      Should be parallel unless future iterations/loops are impacted by previous ones IMO. Going for the fastest synchronous/sequential approach is a great exercise, but independent iterations are the perfect Use Case to be done in parallel.

    • @marbachdaniel
      @marbachdaniel Рік тому +1

      It might be a good idea to add a benchmark consumer type to actually consume the iteration result to make sure nothing gets optimized away

  • @coding-gemini
    @coding-gemini Рік тому

    Very interesting to learn this, I could use this in my project. Thanks Nick

  • @tronaitor0
    @tronaitor0 Рік тому

    Love these kind of videos, and so well explained!

  • @kevinmartin7760
    @kevinmartin7760 Рік тому +2

    As an old guy, I want to add that, if you don't care about the order of iteration (and all the parallel examples illustrate this is the case here), you can run the index backwards, which avoids calling Count, Size, or Length on each iteration:
    var asSpan = CollectionsMarshal.AsSpan(_items);
    for (int i = asSpan.Length; --i >= 0; )
    {
    var item = asSpan[i];
    }
    Note that I explicitly declared i as a signed type so the loop termination condition can be satisfied.
    Many of the other examples (not using Span) also fail if the collection is changed during the iteration. The difference is that with the cases that use an enumerator you deterministically get a specific exception, whereas with the Span you just get mysterious behaviour (which is also true for the direct indexing loop).

    • @alexintel8029
      @alexintel8029 Рік тому +1

      Just a couple of days ago, I implemented a backward for loop similar to your example and it used a var for the index variable. I wonder if is it really worth using --i >= 0 ?

    • @kevinmartin7760
      @kevinmartin7760 Рік тому +2

      @@alexintel8029 It depends on the actual processor, but on the ones typically used nowadays comparing the result of a computation with zero is faster because no compare instruction is required. The instruction for the computation (in this case likely a decrement instruction) will set flags in the processor indicating if the result was zero, negative, or the computation produced signed or unsigned carry/borrow/overflow, so it can be immediately followed by a conditional jump.
      If you separate the decrement from the compare, a decent optimizing compiler should be able to relocate them so the conditional jump is still right after the compare, for instance treating:
      for (int i = x; i >= 0; --i) {...}
      as:
      int i = x;
      if (i >= 0)
      do {...} while (--i >= 0); // which can again decrement and conditionally jump with no compare
      instead of the more direct
      int i = x;
      while (i >= 0) { ...; --i; }
      However if your exit condition compares with a value other than constant zero, as in
      for (int i = 0; i

    • @alexintel8029
      @alexintel8029 Рік тому

      @@kevinmartin7760 Thanks for the explanation Kevin.
      I realise the beauty of your original example
      for (int i = asSpan.Length; --i >= 0; ) {...}
      It helps
      a) prevent accessing asSpan[asSpan.Length] which would lead to index out-of-bound error
      b) decrement the loop
      c) test for exit condition

  • @rogerdeutsch5883
    @rogerdeutsch5883 Рік тому

    Fantastic video, great info and very clear. Learned a lot. Subscribed.

  • @paulembleton1733
    @paulembleton1733 Рік тому

    Didn’t know about Span(), never would have thought to look, foreach was already heaven, thank you.
    I was taught never add or remove items during a for loop but dd it anyway, then fast forward to writing multithreaded applications and foreach and Span() throwing an exception is a useful indicator of faulty design.

  • @jondoty
    @jondoty Рік тому +1

    LINQ doesn't have a ForEach extension method. What's being used in the video looks like the ForEach method defined by List.

  • @hipihypnoctice
    @hipihypnoctice Рік тому +11

    Interesting. I could use this to speed some things up. Seems pretty niche for most of what I do tho, but definitely an improvement where improvements can be made

  • @cdarrigo
    @cdarrigo Рік тому

    Excellent find. Thank you

  • @chiragdarji1571
    @chiragdarji1571 Рік тому +3

    hi, great video as usual. Do you take topic suggestions? parallel.foreach vs parallel.foreachasync pls .. :)

  • @vladkorsak2163
    @vladkorsak2163 Рік тому

    Good point man. Thanks for the video.

  • @redguard128
    @redguard128 Рік тому +7

    It was as expected. Nothing beats the standard WHILE loop except doing things in parallel - which comes with a lot of downsides.

  • @zacky7862
    @zacky7862 Рік тому

    Oh wow! didn't know about this. Thank you so much

  • @slipoch6635
    @slipoch6635 Рік тому

    Always great info man.

  • @stephencollis1453
    @stephencollis1453 Рік тому

    I'm watching this for fun, it's legit fun to watch. Hat's off

  • @Sky4CE
    @Sky4CE Рік тому

    That is awesome! Thanks Nick

  • @Meta0Riot
    @Meta0Riot Рік тому

    Awesome video. Always learn something new.

  • @franciscovilches6839
    @franciscovilches6839 Рік тому

    Very interesting. Thanks for this!

  • @beecee793
    @beecee793 Рік тому

    Great video, glad I found you.

  • @hamedsalameh8155
    @hamedsalameh8155 Рік тому +1

    This is really amazing Nick! Even in the things that seem so basic in simple, we are finding hidden gems!

  • @miguelfajardo8236
    @miguelfajardo8236 Рік тому

    What a great video and information for iterating a List !! I think that this works with a List of object that has plenty of properties...

  • @docleoyeo
    @docleoyeo Рік тому

    This helped a lot thank you

  • @tobyjacobs1310
    @tobyjacobs1310 Рік тому

    This is so much nicer than my in a pinch method:
    Compiled reflection accessing the array, then use that and the count to get a span. Probably a smidgeon faster too...
    Spans are amazing....

  • @neotechfriend
    @neotechfriend 2 місяці тому

    As always , thanks Nick

  • @ivaniliev93
    @ivaniliev93 Рік тому

    Amazing stuff, thanks!

  • @dawidopalinski702
    @dawidopalinski702 Рік тому

    This is the best free software Ive seen. Respect.

  • @BadgersEscape
    @BadgersEscape Рік тому +3

    Getting a local scoped reference to the array allows JIT to optimize away the range checks in the loop (technically also unroll but I don't think it does that). It's not possible for a list since there is no guarantee that other code somewhere wont change the length during our looping. But if you have an array, then length is fixed, and you can do a single if-check pre-looping instead of checking the bounds every iteration.

    • @TheMAZZTer
      @TheMAZZTer Рік тому

      Interesting, this should also be possible for any foreach since the collection isn't allowed to change during the loop, but I guess .NET does not implement that optimization (yet).

  • @jongeduard
    @jongeduard Рік тому +1

    Probably the absolute winner: on Stackoverflow I found an example of doing parallel work on a Span, but it involves unsafe pointers, since the Span type itself cannot escape to the heap and therefore Parallel calls on it are normally not possible.
    I searched for this because I was curious why this ultimate combination was not metioned in the video.

    • @pedroferreiramorais9773
      @pedroferreiramorais9773 Рік тому +1

      You can actually combine Parallel.ForEach and Span without pointers. I did some benchmarks and it was faster than all of Nick's implementations @ 1 million elements.
      The trick is using Partitioner.Create(0, list.Count) and passing it to Parallel.ForEach along with a closure around list that takes a Tuple as parameter and marshalls list to Span, then slices it using the tuple and finally iterates over it.

    • @jongeduard
      @jongeduard Рік тому +1

      @@pedroferreiramorais9773 Oh great :), I really have to dive into that to understand how that works.
      Let's say that a very simple solution does at least not exist yet.

    • @pedroferreiramorais9773
      @pedroferreiramorais9773 Рік тому

      @@jongeduard well, you can create an extension method to encapsulate all the logic, but it loses much of the performance gain. It can still be better than sequential span iteration of very large lists/arrays, but if performance is the main concern, you often have to get your hands dirty.

  • @matejakendereski4264
    @matejakendereski4264 Рік тому

    Everything is cool! Thanks!

  • @22Ericelcuervo
    @22Ericelcuervo Рік тому

    Hi, great video! its so usefull. Thanks. I have a question, how can i implement it in async method? what is the best practices in async?

  • @haxi52
    @haxi52 Рік тому

    I rarely work with List, would have liked to see benchmarks with lists of classes. I feel like the results would be very different.
    Also please add disclaimers to your performance videos. Some might get the idea they should be using span loops everywhere.

    • @nickchapsas
      @nickchapsas  Рік тому

      There is a similar performance improvement with other objects. The video does have a disclaimer too

    • @MaxReble
      @MaxReble Рік тому

      I ran the test and on my machine results are as followed (with dotnet 7)
      List
      Iterate_ForEach | 49.71 us | 0.330 us | 0.276 us | - |
      Iterate_For | 40.59 us | 0.241 us | 0.213 us | - |
      Iterate_ForEach_AsSpan | 24.64 us | 0.129 us | 0.121 us | - |
      List
      Iterate_ForEach | 52.27 us | 0.634 us | 0.562 us | - |
      Iterate_For | 41.18 us | 0.477 us | 0.398 us | - |
      Iterate_ForEach_AsSpan | 25.33 us | 0.480 us | 0.426 us | - |
      So, I see a bigger difference between for and foreach as nick does, but the delta of for and foreach_asspan does not change.

  • @AbhinavKulshreshtha
    @AbhinavKulshreshtha Рік тому +3

    80085, I see a man of refined culture. 😅☺️

    • @krccmsitp2884
      @krccmsitp2884 Рік тому +1

      The zip code of the Simpsons' home town Springfield.

    • @AbhinavKulshreshtha
      @AbhinavKulshreshtha Рік тому +1

      @@krccmsitp2884 I didn't knew that.. I was thinking about the old calculator trick we used to do in schools during mid 90s, when we first got to use them.

    • @krccmsitp2884
      @krccmsitp2884 Рік тому

      @@AbhinavKulshreshtha Well, that's the other meaning. I know that trick too and what you wanted to indicate. :-)

  • @Evan-zj5mt
    @Evan-zj5mt Рік тому +1

    Watching your videos makes is very humbling and makes me realise that I'm absolutely shit at my job!

    • @nickchapsas
      @nickchapsas  Рік тому +4

      Nah trust me you don’t need to know 99% of the stuff I show to be good at your job

  • @yv989c
    @yv989c Рік тому

    Thanks! I wasn't aware of CollectionsMarshal!

  • @DaveGouda
    @DaveGouda Рік тому

    This was really interesting. I've never seen the AsSpan methods before. I honestly had to look up what a Span was lmao.

  • @KineticCode
    @KineticCode Рік тому +11

    i think the unsafe part is fine and expected, foreach breaks if you add/remove elements during a loop as well.

    • @RahulSingh-il1xk
      @RahulSingh-il1xk Рік тому +2

      That's true. But what if we mutate objects of the list. Say, a person from List while looping. Foreach allows this - will this span approach too?

    • @PetrVejchoda
      @PetrVejchoda Рік тому

      @@RahulSingh-il1xk Obviously not on values that you access during the iteration. What I am interested in is what happens if I mutate values, that are not accessed during the iteration.

    • @KineticCode
      @KineticCode Рік тому +1

      Guys I think you can mutate objects :) its not a readonly span, just a span

    • @Crozz22
      @Crozz22 Рік тому

      because foreach breaks if the list is mutated then they should just make foreach compile into the unsafe part

  • @bmazi
    @bmazi Рік тому

    Extra way: walk via unconditional (!) "for" loop, exit via catching "OutOfBounds" exception. Removes double-checking of bounds, but introduces overhead from exception handling. May outperform if the list is extremely huge (throw cost is constant and doesn't scale with items count).

  • @brunodossantosrodrigues5049

    How much time did you train to talk so fast and clearly at the end of the video? I really thought that I knew how to iterate... thanks for always taking our code to the next level

  • @jackp3303
    @jackp3303 Рік тому

    There is one more way to iterate - using SIMD (Vector), which has lowest readability, but probably will have the best performance, because it's accelerated on hw level and CPU takes n items in processing at one CPU clock.

  • @ayudakov
    @ayudakov Рік тому

    Thank you!

  • @zagoskintoto
    @zagoskintoto Рік тому

    Great video! I'm curious, is Span still faster if there's heavy processing of the data done inside the loops? What if (as a separate question) you were to perform async operations like retrieving data from a DB inside your parallel loop, implemented with the Parallel.ForEachAsync method? How does it compare when just using Span?

    • @ImmoLandwerth
      @ImmoLandwerth Рік тому

      You can’t use await in a method that uses spans

  • @QwDragon
    @QwDragon Рік тому +32

    ForEach is list method, not linq method.
    Span forbids only adding and removing of items, but not assigning.
    And also I don't like benchmarks that don't use data. Some optimizer can remove more than expected. You've shown IL, but it doesn't garantee jit won't change smth.
    9:26 How can 1 byte be allocated?

    • @VoroninPavel
      @VoroninPavel Рік тому

      I assume there are cases when compiler (jit) can infer that it's safe to use span for list traversal.

    • @2003vito
      @2003vito Рік тому +1

      malloc(1)

    • @protox4
      @protox4 Рік тому +3

      1 byte is allocated because the JIT allocates, and the benchmark picks it up. That allocation is removed in .Net 7. You can read more about it on the benchmarkdotnet repo.
      It's actually more than 1 byte allocated, but the benchmark divides it by how many iterations were ran.
      [Edit] Actually, the rogue allocation still seems to be showing up in Net 7, but the cause hasn't been looked into yet.

    • @QwDragon
      @QwDragon Рік тому

      @@protox4 Thanks for pointing out is is averaged.

  • @codemonkeyjesse
    @codemonkeyjesse Рік тому

    Looking at the implementation of List.ForEach I don't understand why this one is so much slower than just the standard for. Is invoking an Action really that slow of an operation?

    • @nickchapsas
      @nickchapsas  Рік тому +2

      Yes and it is also prone to closures that can make the problem even worse

    • @Crozz22
      @Crozz22 Рік тому

      @@nickchapsas what if you make the lambda static

  • @elpe21
    @elpe21 Рік тому

    foreach statement cannot operate on enumerators of type 'Span.Enumerator' in async or iterator methods because 'Span.Enumerator' is a ref struct. Has that changed in .Net 7?

  • @SergeDuka
    @SergeDuka Рік тому

    To span a loop into a 13 minute video, that's what I call a great job! lol

  • @aschwinwesselius
    @aschwinwesselius Рік тому

    @Nick, I'm curious if you've ever encounter a Duff's Device in C# and if you would consider it to benchmark it as well. I would be delighted to see it compared to the span.
    Thanks for this video in any case.

    • @MarkusSchaber
      @MarkusSchaber Рік тому

      C# Syntax is much more restrictive than C, it won't allow the original Duff's Device. And, tbh, I'm rather happy about that. 🙂

  • @parthaf22
    @parthaf22 Рік тому

    Great video. could you make one video on Partitioner?

  • @CricketThomas
    @CricketThomas Рік тому

    The puts so much perspective on things 😅

  • @micmacha
    @micmacha Рік тому

    Thank you, I literally didn't even know about spans.

  • @karldavis7392
    @karldavis7392 Рік тому +1

    I use Parallel when the tasks are small in number and heavy. I have an app that does six similar tasks, each taking about 500 ms, and it's great. If you needed 3000 ms instead by doing 3000 tasks that each take 1 ms, the overhead of creating each instance makes it a close call. If it's 3,000,000 jobs that each take 1 us, then I definitely would not parallel.

  • @lancemarchetti8673
    @lancemarchetti8673 Рік тому

    Brilliant!

  • @petrusion2827
    @petrusion2827 Рік тому +5

    Calling it before watching the video: CollectionsMarshal.AsSpan()
    Edit: Called it. I would've also liked to see a benchmark which uses the list cast to an IEnumerable, then the IEnumerator would be an interface variable instead of a stack struct which would slow things down because of dynamic dispatch.

    • @nickchapsas
      @nickchapsas  Рік тому +2

      Hey I can see that you skipped forward 👀

    • @petrusion2827
      @petrusion2827 Рік тому

      @@nickchapsas Oh yeah I didn't have time to watch the whole video from start to finish, I used UA-cam's 10 second skips to go through the most relevant parts, but only after I made the comment :D
      Nice vid for sure

    • @QwDragon
      @QwDragon Рік тому

      @@nickchapsas you haven't shown foreach on IEnumerable in the video.

  • @ceosmangumus
    @ceosmangumus Рік тому +1

    Foreach also has the same limitation, can't add or remove from the list, then we can replace each "foreach" with span version?

  • @ayhamala3ma189
    @ayhamala3ma189 Рік тому

    Brilliant 💪

  • @mykhailokonontsev3132
    @mykhailokonontsev3132 Рік тому

    Love your seeds

  • @deus_nsf
    @deus_nsf 3 місяці тому

    This is super interesting! I never learned about "spans" in any school, I don't even know what it is! I will eventually google it but could you elaborate quickly in case my laziness gets the better of me? 😅Thanks!

  • @titiksasanti2205
    @titiksasanti2205 Рік тому

    Thank you!

  • @nickst0ne
    @nickst0ne Рік тому

    When a program's performance tests are below client's expectations, would you always go for a Span refactorization?
    ...assuming that no major blunder was made like a bad algorithmic complexity.

  • @ahmedseada7371
    @ahmedseada7371 Рік тому

    Great Thanks

  • @diadetediotedio6918
    @diadetediotedio6918 Рік тому

    I will give you a little tip, on the List as Span method you can actually use it to modify the items with ref, like:
    var listSpan = CollectionsMarshal.AsSpan(list);
    foreach(ref var item in listSpan)
    {
    // You can modify 'item' here even if it is a struct
    }

    • @nickchapsas
      @nickchapsas  Рік тому

      You don’t need ref to modify the items. You can just modify them. They are still references

    • @diadetediotedio6918
      @diadetediotedio6918 Рік тому +3

      @@nickchapsas
      No, if they are structs you will surely need ref, and if you want to "modify" immutable records too.
      Think in things like:
      foreach(ref var itemStats in item.Stats)
      {
      itemStats = itemStats with { Speed = 10 };
      }
      You cannot do anything like that without refs.

  • @CharlesBurnsPrime
    @CharlesBurnsPrime Рік тому +3

    I did not expect to wake up this morning and be surprised by a genuinely faster way to iterate a list, one of the most common tasks in software development.

  • @GregUzelac
    @GregUzelac Рік тому

    Outstanding comparison. 5 stars

  • @swordblaster2596
    @swordblaster2596 Рік тому

    I doubt I'll ever use it, but quite amazing.

  • @T___Brown
    @T___Brown Рік тому

    Nice. Ty

  • @AaronMolligan
    @AaronMolligan Рік тому +1

    I am only 2 weeks into the learning how to code and actually learning c#. What you talking about and showing looks great and fun but it's to much for my brain is right now. Your videos are cool and very informative but just to advanced or geared to seasoned coders. But nice videos, one day I'll fully understand all the things you explain in your vids.

    • @matthewjohnson3656
      @matthewjohnson3656 Рік тому +1

      I’ve been programming for ten years and I have never heard of a span before

    • @davestorm6718
      @davestorm6718 Рік тому

      @@matthewjohnson3656 Same here (25 years!) and just heard of a span a month ago (that isn't )!

  • @RCYmacau
    @RCYmacau Рік тому +1

    Nice video.
    I am also interested if there are any actual difference between Span and an Array, which in my opinion should perform more like the same.

    • @nickchapsas
      @nickchapsas  Рік тому +4

      There is a difference indeed. I’ve covered this in the dedicated span video if I remember correctly

    • @user-pn1gt2km5r
      @user-pn1gt2km5r Рік тому

      @@nickchapsas there will be no performance difference iterating Array vs Span, and why would there be - both represent contiguous memory with minimum overhead. List is a different story because it has some additional logic, and you are paying the (small) price for it.

    • @renauddanniau676
      @renauddanniau676 Рік тому

      @@user-pn1gt2km5r Actually there is. I also have noticed that

  • @user-qf2xk1fg6e
    @user-qf2xk1fg6e Рік тому

    Hello! At my job, we had an ague with colleagues what is better if you need to represent a collection in your DTO: List or Array. I think array is better. I had my own bunch of arguments about this, but would love to listen to your opinion about this question. Is there any chance you will make a video with close topic?

    • @Isr5d
      @Isr5d 8 місяців тому

      depends on the usage. For DTOs (since there is no mutations) I prefer to use IEnumerable (most of the time) because it's implemented on almost all collections (Arrays, Lists ..etc). This makes things much easier to work with, and to avoid adding more memory overheads. If you want to only read, then just iterate. If you want to mutate, then copy it to any desired collection type (ToList, ToArray, ToHashSet ..etc.).

  • @user-lf5sy5mv3l
    @user-lf5sy5mv3l Рік тому

    It was funny when I rolled my eyes before you said “Of course it would be a span” and started giggling 😂😂

  • @justengineering1008
    @justengineering1008 Рік тому

    To complete the big picture, You can add Enumerator + While( list.MoveNext()){bla-bla-bla}
    some tests say that it more efficient than foreach/for but not so efficient as a span

  • @engineeranonymous
    @engineeranonymous Рік тому +2

    If you are algorithm is loop heavy, you can use loop unrolling for more speed.

  • @exley51
    @exley51 Рік тому

    Hi Nick. I tried this using Rider in macOS with the arm64 version of .NET 7 and I get very different results. The regular foreach is much slower than For in every test for 100, 100K and 1M. More than double for 1M. Any reasons come to mind?

  • @gergelycsaba5008
    @gergelycsaba5008 Рік тому +1

    1:35 Nice seed value :)

  • @darkclove7365
    @darkclove7365 Рік тому

    So only way to grantee the collection is not mutated while using CollectionMarshal is to lock it? Or would it be safe to use if I wanted, let's say get a list of Entities I wanted to write out on a page? I mean, what collections couldn't be mutated at any given time when you haven't locked it?

  • @Faygris
    @Faygris Рік тому

    Your channel is a pot of gold for me as a bad C# developer ⭐

  • @vredurs
    @vredurs Рік тому +1

    I have a question regarding the iteration with span. Is it ok to modify the objects in the array as long as I don’t add or remove? Eg changing or updating a property? Thanks in advance

  • @TheCoastpear
    @TheCoastpear Рік тому +1

    How in the world do you find this stuff? 🤔 great video by the way ;)

  • @billy65bob
    @billy65bob Рік тому +2

    I'm pretty surprised, so they optimised out the allocation of the foreach enumerator?
    And somehow also eliminated the overhead of its constant 'version' checks (to stop changes mid iteration)?
    I wonder if this is a consequence of that 'sealed' keyword you mentioned the other day, or if other work also went into it

  • @B08AH
    @B08AH 9 місяців тому

    I wonder, can you mutate the list when other iteration methods are used? No side effects expected?

  • @jeffreyblack666
    @jeffreyblack666 Рік тому

    I would say that with the error listed, it is only the 100 item list that is different for the For and ForEach.
    You can even see that when you run all of them options, you get the order the other way around for the 1 million item list.

  • @nove1398
    @nove1398 Рік тому

    Very interesting stats here

  • @borislavborislav5545
    @borislavborislav5545 Рік тому

    You forgot one more exotic way:
    var numbers = Enumerable.Range(1, 1_000_000).ToList(); ;
    int idx = 0;
    start:
    if (idx < numbers.Count)
    {
    idx++;
    goto start;
    }

  • @markharby180
    @markharby180 Рік тому

    Nice one, could help increase complex reporting.

  • @irjgametube2995
    @irjgametube2995 Рік тому

    INSANE!

  • @harag9
    @harag9 Рік тому

    Wow impressive results, depending on what work you need to do within the loop, a Parallel span loop might be good.

    • @jongeduard
      @jongeduard Рік тому +1

      See my other reply, it's not possible without unsafe pointer tricks, due to the fact that a Span cannot escape to the heap. It simply won't compile.
      Parallel loops make use of callbacks (delegate, lambda, which is basically a reference type object) and therefore need an allocation in the managed heap.

    • @harag9
      @harag9 Рік тому

      @@jongeduard Ahh thanks. I've never used a span - still use 4.8 :(

    • @jongeduard
      @jongeduard Рік тому +2

      @@harag9 Someone else replied to my other command that it is actually possible. :P
      Something with Partitioner.Create and a Tuple. I would still need to dive into that one and try it myself to better understand it.
      Let's conclude that a simple solution does at least not exist.

  • @TheMAZZTer
    @TheMAZZTer Рік тому +5

    You can't mutate the collection in any foreach anyway so there's no downside to using the span method it would seem to me. So if you need to mutate the list you're using for instead of foreach anyway and just can't use the span.
    Very cool!

    • @jasonx7803
      @jasonx7803 Рік тому +1

      If you're running parallel code something else might be mutating the collection.

    • @lnagy88
      @lnagy88 Рік тому +1

      @@jasonx7803 Then maybe you shouldn't use a list and try something more thread safe.

    • @PelFox
      @PelFox Рік тому

      It's limited to List though, wished it would have worked for IEnumerable or IReadOnlyCollection. Very rarely do I need to iterate a List without doing some work on the data, then I would use a IReadOnlyCollection or IEnumerable.

    • @jasonx7803
      @jasonx7803 Рік тому

      @@lnagy88 I'm just responding to the idea that "there's no downside". Using a forEach isn't thread safe just because it can't modify the data, you still have to make sure nothing else is modifying it.

  • @ItsTheMojo
    @ItsTheMojo Рік тому +2

    The only approach that doesn't throw an exception if the collection changes, at least as far as I know, is a for loop. Anything that uses an iterator will throw because the MoveNext method checks the version. So that covers foreach and the List.ForEach methods at least. The parallel ones almost certainly won't handle a collection change during iteration. Most of the time,
    changing a collection while iterating over it in any way is undesirable.