Are you using the Stopwatch efficiently in .NET?

Поделиться
HTML-код
  • Опубликовано: 27 окт 2024

Комментарии • 77

  • @PointlessMuffin
    @PointlessMuffin Год назад +76

    Can you cover TaskCompletionSource? It is one of the lesser known gems in C#

    • @ivandrofly
      @ivandrofly Год назад +2

      SingletonSean has a nice video about it. I think he also talked about the topic of his Task.Continue video / async await in constructor

  • @knightmarerip711
    @knightmarerip711 24 дня назад

    Once again I have been enlightened by your vids! I'm definitely going to start using this... and probably update my old code to use this as well. Thanx for sharing! Keep up the good work!

  • @clashclan4739
    @clashclan4739 Год назад +4

    Definitely new one in .net 7. And nick this is what makes your channel special. very unique content. Thank you so much ❤️

  • @JM-in8fq
    @JM-in8fq Год назад +3

    I made my own class, named Hourglass, implementing IDisposable and printing the elapsed miliseconds to the logs when it goes out of scope.

    • @nickchapsas
      @nickchapsas  Год назад +4

      Sounds very inefficient

    • @user-zz6fk8bc8u
      @user-zz6fk8bc8u Год назад

      @@nickchapsas if it's a class then yes because it would lead to a lot of heap allocations if called a lot. But with stopwatch as a struct the performance would be the same as the new .net 7 stuff and also without any allocations (besides the stuff you allocate for logging which you would also have to allocate using the new methods). I have a PoC and some benchmarks but I'm not sure how to post that on yt without triggering anti-spam filters.

  • @slashd
    @slashd Год назад +1

    Im totally gonna use this new way of using StopWatch! 👍

  • @moditrix
    @moditrix Год назад +5

    Yes, I use StopWatch in the dispose pattern and wrap it by writing to Prometheus metrics. It is a convenient way to get an overview of the processing times of the measured methods. Prometheus in combination with Grafana can then provide detailed information about the entire system.
    Manual management with care of variables is tedious and stereotypical work, you have to solve all the branches of the method where the code ends.
    I take a bit of memory with the dispose pattern, but again it's comfortable and easy to control.
    After tuning the code, just comment one line where I create the instance using "using". I have good experience with it.

    • @user-zz6fk8bc8u
      @user-zz6fk8bc8u Год назад +3

      couldn't you also make the dispose solution make heap allocation free by returning a struct that only holds a single long (start_time) and a reference to whatevery logging you need and that struct than implements IDisposable (structs can also implement interfaces) and in the Dispose Method call GetTimestamp again and calculate the diff and call your logging framework. The would result in zero heap allocations and the reference for prometheus is probably long-lived / singleton anyways.

    • @moditrix
      @moditrix Год назад

      @@user-zz6fk8bc8u You're right, that's how it could behave. The prometheus instance is a singleton. I will try ;)

  • @GuildOfCalamity
    @GuildOfCalamity Год назад

    I typically use a home-brew version of an overloaded Stopwatch class that inherits from IDisposable and employs a using scope so that it can cleanup after itself upon finishing.

  • @aidarsharipov3751
    @aidarsharipov3751 Год назад +6

    Can you please make video about file uploading and downloading? I think it is one of the most important thing in c# and it is not fully understandable by many users, because there are several ways to implement it, but which one is the best one, which one is the most fastest and optimized?

    • @rolandtennapel5058
      @rolandtennapel5058 Год назад

      There is no 'best' one, just best practices you should take into account. It all depends on what you are up-/downloading (does it need validation like a data file or are gaps acceptable like media files?) from where (fileserver, databaseserver, heck, even internally between motherboard and graphics cards?), how (streaming it for direct use or caching it for editing or analysis) and why (do you need to interact with parts of the file but not other or do you need to interact with the entirety?). These are just some questions that come to mind because it relates to you transferring this data in full or in part, what are the limitations you are facing (this one especially seems overlooked a lot, when an end-user has to work with a system in the field where internet is unreliable or the device they are using should be doing more than just up-/downloading, bottlenecking its uses)... Just so many questions spring to mind when reading this question... 😅

  • @diadetediotedio6918
    @diadetediotedio6918 Год назад +4

    I think this may be of some value in the case of creating a time checking solution per request in an API for example, using a stopwatch struct you would reduce the necessary allocations to deal with this (not that it couldn't be solved before but, whatever) .

  • @CharlesBurnsPrime
    @CharlesBurnsPrime Год назад

    I love this stuff. Eliminating admissions is a good deed and a fine hobby. I am writing this valueless comment to help Nick with RUclips's algorithms.

  • @rolandtennapel5058
    @rolandtennapel5058 Год назад

    Interesting, will play around with this and see more of the differences. General rule of thumb; Whenever you save in memory, you use more in processing, but this just seems more efficient since this processing is done in the background anyways... 🤔

  • @UmanPC
    @UmanPC Год назад +1

    Like.
    Very useful.
    Tnx Nick.

  • @razielspc
    @razielspc Год назад +1

    bit of a random question but im new to rider and i was wondering , should i use it as is ? any must have extensions? or tips about it? would really appreciate it!

  • @F1nalspace
    @F1nalspace Год назад +1

    A few years ago i wrote a profiler, that uses RDTSC and the current ticks. Initially the ticks was read from the win32 API (QueryPerformanceCounter) directly, but later i dropped that and used Stopwatch.GetTimestamp() instead. So QPC is easy, but RDTSC on the other hand was not, because you need to execute specified assembly code to query it directly from the CPU. In C/C++ this is stupidly easy, on C# not so much. You need to pass in the assembly code to Marshal.GetDelegateForFunctionPointer(). To prevent getting the function pointer over and over again, we simply statically cached the function call on the very first call. This worked very great and helped us a lot to identfy several performance issues over the years.
    Nowadays we fully switched to benchmarkdotnet, but we occasionly activate the profiler to get the tree-based timings for specific code-blocks or methods.

  • @paulegan3783
    @paulegan3783 Год назад

    Thanks!

  • @Petoj87
    @Petoj87 Год назад +2

    Would have been nice to compare the raw solution with the ValueStopwatch solution, I'm guessing the ValueStopwatch has a little bit overhead but not much?

  • @alexanderyermakov5437
    @alexanderyermakov5437 Год назад

    I wrote objects pool service for calculating request handling time. Interesting that it was yesterday 😃

  • @peterrexjoseph
    @peterrexjoseph Год назад +2

    Of course Stopwatch but when it comes to enterprise solutions, didn't find find the allocation of memory on Stopwatch being an issue rather the enterprise solution is a challenge to solve which goes above seconds due to different dependencies. But looks good to breakdown the dependencies and test the component level which might help with this improvement.
    But have we really gained that much of a difference with the new approach? I don't think in a single method we use lot of Stopwatch instance

    • @paulkoopmans4620
      @paulkoopmans4620 Год назад +7

      Greatly depends on what you are writing I guess. Memory usage and garbage collection pressure is indeed not always a big problem for everybody. Desktop apps or maybe running on a local network beefy server.
      BUT..... You have an API method where you simply use Stopwatch.StartNew();
      Your load is 6000 requests/s. In one second you have just allocated 235 KB. Do it on another 10 methods and you have just allocated 2,3 Mb. So, your system does not even have to be large to make 40 bytes apparently turn into 2.5 MB.
      Did you also start using Stopwatch.StartNew(); to calculate any sub-operations?
      With not much more imagination you could see that you might be adding 25 or even 50MB in a matter of seconds.
      When in the cloud.... this "behaviour" will cause you money. Both in requirement of needed ram as well as your app running extra GC.... which effectively will slow your app down.

  • @Misteribel
    @Misteribel Год назад

    Your benchmark may not measure what you think it does. The JIT will likely elide the DoSomething (check x64 asm), since it’s empty. T may or may not detect that the for loop can be erased as well (I’ve seen it done). BenchMarkDotNet has a property you can use to dump the x64 asm, which can help in tight spits like these.

    • @nickchapsas
      @nickchapsas  Год назад

      It won’t. It’s measures and does exactly what I think it does

    • @Misteribel
      @Misteribel Год назад

      @@nickchapsas interesting. I had almost exactly the same test and the empty method totally disappeared. Since then I’m on high alert wrt certain types of benchmarking, often checking the x64 asm. I’d expect the JIT to inline an empty method, curious that it didn’t in your case.

  • @IceQub3
    @IceQub3 Год назад

    We used to pool the stopwatch using a concurrent bag

  • @chj915
    @chj915 Год назад

    The main reason that I use StopWatch is to save that extra typing on "var startTs = new DateTime();" thing.

    • @nickchapsas
      @nickchapsas  Год назад +1

      Stopwatch and DateTime now do fundamentally different things

  • @engineeranonymous
    @engineeranonymous Год назад +2

    Does @keepcodingpodcast channel belongs to you ? The Channel has your video with Damian Edwards.

  • @limbique
    @limbique Год назад

    What does ABP has to do with the StopWatch.

  • @MichaelJordan-jv6ic
    @MichaelJordan-jv6ic Год назад

    Unless I'm in a super-critical path, I've always found it sufficient to use DateTime. I'm sure Stopwatch has its advantages, though.

    • @nickchapsas
      @nickchapsas  Год назад +3

      The problem with DateTime isn’t performance. It’s accuracy

  • @rockerviktor
    @rockerviktor Год назад

    What I don't really get is how this is different from var timeElapsed = DateTime.Now - DateTime.Now? What's the point using the Stopwatch class for getting the current time?

    • @nickchapsas
      @nickchapsas  Год назад +5

      It's very different. DateTime.Now won't be accurate enough because of how much it's doing in those properties to calculate the value. There is overhead that the GetTimestamp method of the Stopwatch class doesn't have. The stopwatch will use OS specific imported methods and interop with them to get the value straight from the OS. DateTime.Now first gets DateTime.UtcNow, then calls TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc to get the offset, then does some timzone specific calculations and then eventuallt it gives you the DateTime back

    • @rockerviktor
      @rockerviktor Год назад

      @@nickchapsas Thanks Nick! We are friends now.

  • @mk72v2oq
    @mk72v2oq Год назад +8

    Interesting why MS does not stick with "use value types (structs) as much as possible" in standard library from the beginning. This feature allows to outperform e.g. Java dramatically in certain scenarios. Strange that they are not used it at least for marketing purposes back in the day when C#-Java war was the thing.

    • @nickchapsas
      @nickchapsas  Год назад +5

      Structs aren't always better. Because they are value types and they are copied around, if they are big they can cause performance issues

    • @diadetediotedio6918
      @diadetediotedio6918 Год назад +3

      Because classes are much more OOP, and C# originally was mean't to be very OOP-based, then the thing with hidden fields and properties and using classes everywhere, back in the time GC was a cool thing. Structs also aren't very efficient all time, for example, if you want to pass your types around you will need to pin the into some class, because structs are copied in memory and not just refereed as classes

    • @mk72v2oq
      @mk72v2oq Год назад +1

      @@nickchapsas yeah, the language itself is not very designed for it. But this particular issue can easily be mitigated by using *ref* keyword.

    • @nickchapsas
      @nickchapsas  Год назад +1

      @@mk72v2oq It can, if you know exactly what you're doing because there is also the understanding that value types are copied so you need to keep track of what's copied and what's referenced which can lead to tricky situations. You also need to know when to use the in keyword and where not to. It's why you rarely see this feature used correctly

    • @mk72v2oq
      @mk72v2oq Год назад

      @@diadetediotedio6918 I also think classic OOP was the main reason. Composition over inheritance and parametric polymorphism were functional languages thing back in the day. And such paradigm shift is a relatively recent thing for imperative languages.
      For the copying issue, you can just use *ref* keyword, which passes structs by reference, as the name implies.

  • @XenidRol
    @XenidRol Год назад

    I use a stopwatch on my phone, no allocations at all!

  • @shahfaisal3923
    @shahfaisal3923 Год назад

    I want to be a pro like you............!

  • @urbanelemental3308
    @urbanelemental3308 Год назад

    I recently created a TimeStamp struct that has nice implicit conversions and uses the GetTimestamp() method to get the underlying value. So we are all thinking the same thing.
    You can easily do this without .NET 7 by diffing the next GetTimestamp() call as ticks.

    • @wvvwwwvvw
      @wvvwwwvvw Год назад

      Could you share the src code please? I just realized that this (edit: idea from the video) is only part of .NET7

  • @thomasschroter3802
    @thomasschroter3802 Год назад

    I am not using the stopwatch at all... Should I?

  • @ved_s
    @ved_s Год назад

    Is there a performance difference between using Stopwatch and DateTime.Now to measure time?

    • @nickchapsas
      @nickchapsas  Год назад +3

      Yes there is. DateTime.Now (DateTimeOffset.Now/TimeOnly) have more overhead and can't give as precise measurements mainly because they take into account Ufc offsets etc. Also, even though structs, those values are pretty heavy which add extra overhead. Stopwatch will use library imports based on your environment to get the timestamp value straight from the machine.

    • @christianj.2581
      @christianj.2581 Год назад +10

      If you want to measure a time span between two events, using DateTime.Now is the wrong approach. Besides the performance overhead and accuracy issues (depending on the processor/OS the minimum time step can be much larger than you'd think - back in the day it was often in the order of ~14ms), the even more pressing issue is that it's simply wrong, because DateTime.Now does not necessarily progress forwards in time, or in a linear/constant fashion. If you happen to measure between daylight savings time changes the time difference can suddenly be one hour more, or less, than you'd expect. Even without DST changes, the system clock may not be running quite right and whenever Windows decides to synchronize its clock with the time server you may experience sudden jumps. Stopwatch is the only reasonable approach to measure elapsed time between events, as it completely removes the quite arbitrary and often surprisingly complex concept of the human calendar/dates/timezones :)

  • @powermetal1963
    @powermetal1963 Год назад

    Of course we use custom methods and/or structs. Are you seriously thinking that we will wait 20+ years MSFT to expose 2-3 static one liners from their "secret" (in fact over encapsulated) code (why are ValueStopwatch and similar internal??). And now even promoting it like a "new" ("modern"??) way. Come on. We have struct variants of most of their stuff from 2004. And C++ has managed pointers (C# refs) from the very beginning. I like the language, but not the way it is extended. Same for BCL. They create and expose useful functionality only when they need it. refs (because of Span>T>), static abstracts (because of the new numeric types) etc.

  • @Danez96
    @Danez96 Год назад

    Wait, this isn't a League of Legends tutorial

  • @paul300
    @paul300 Год назад

    Does anyone has an idea why Rider shows me folders from obj folder and dlls in my project files when opening WIndowsAppSdk projects? (i generated it with the template studio vor Visual Studio 2022)

  • @TechySpeaking
    @TechySpeaking Год назад +1

    First

  • @m1kecrosoft
    @m1kecrosoft Год назад +2

    First ! 😂

    • @paulkoopmans4620
      @paulkoopmans4620 Год назад

      Oh. Thank you so much for your great contribution to the community and the subject at hand. I am sure Nick really appreciates the value you have added to his video as well.

    • @m1kecrosoft
      @m1kecrosoft Год назад

      @@paulkoopmans4620 Sure !

  • @perdonomai8060
    @perdonomai8060 Год назад

    I use DateTime.UtcNow.Ticks (and not DateTime.Now !) to calculate the elapsed ticks. In my case I use it combined with TimeBeginPeriod/TimeEndPeriod (set to 1ms) to ensure precision also in Thread.Sleep(Xms). After proper benchmarking stopwatch is faster so I will replace it!

    • @nickchapsas
      @nickchapsas  Год назад +4

      Yeah anything DateTime won't give you an accureate result, at least not accurate for anything benchmark related.

  • @tubaviewa2624
    @tubaviewa2624 Год назад

    // Should be precise & fast
    var startTime = Environment.TickCount;
    DoStuff();
    var duration = Environment.TickCount - startTime;
    Console.WriteLine(TimeSpan.FromTicks(duration));

    • @nickchapsas
      @nickchapsas  Год назад +2

      Have you actually read what Environment.TickCount is? It is the number of miliseconds passed since you turned on your computer. It can only be precise to the milisecond level which is useless for any meaningful measurement.

    • @tubaviewa2624
      @tubaviewa2624 Год назад

      @@nickchapsas Argh... Right you are. :)
      var stcks = Environment.TickCount;
      var tcs = Enumerable.Range(0, 24).Select(_ => Environment.TickCount - stcks).ToList();
      Console.WriteLine(string.Join(Environment.NewLine, tcs.Select(t => $"{t}: {TimeSpan.FromTicks(t)}")));
      Console.WriteLine();
      Console.WriteLine("Stopwatch.GetTimestamp()/Stopwatch.GetElapsedTime():");
      var tstmp = Stopwatch.GetTimestamp();
      var tss = Enumerable.Range(0, 24).Select(_ => Stopwatch.GetElapsedTime(tstmp)).ToList();
      Console.WriteLine(string.Join(Environment.NewLine, tss.Select(t => $"{t.Ticks}: {t}")));
      Thanx for your videos & your reply! 👍

  • @dmisterb
    @dmisterb Год назад +2

    Do you have a strong opinion on using async/await keywords in a method like this?
    async Task DoSomething() {
    await _service.DoSomething();
    }

    • @THX-ki2sk
      @THX-ki2sk Год назад

      You don't need the async statemachine for such code. This would achieve the same with less overhead:
      Task DoSomething() {
      return _service.DoSomething();
      }

  • @pilotboba
    @pilotboba Год назад

    Is that much different from something like:
    var startTime = DateTime.Now.Ticks;
    await Task.Delay(10000);
    var duration = TimeSpan.FromTicks(DateTime.Now.Ticks - startTime);
    afik those are all structs so no allocations. I've used this before, since I've heard stopwatch wasn't threadsafe. I generally only used seconds or milliseconds depending on what I am timing.

    • @nickchapsas
      @nickchapsas  Год назад

      It is different. Anything in DateTime will do way more than what a simple StopWatch.GetTimestampt() will do which will lead to inaccurate values in the micro scale.

    • @pilotboba
      @pilotboba Год назад

      @@nickchapsas Ok. Maybe you can elaborate. I thought when you showed the stopwatch implementation it was using DateTime behind the scenes.

    • @nickchapsas
      @nickchapsas  Год назад +1

      @@pilotboba Nop, not at all. Here is DateTime.Now: source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/DateTime.cs,1cd5dd408a32124f
      And here is Stopwatch.GetTimestamp(): source.dot.net/#System.Private.CoreLib/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetTimestamp.cs,2882c01d42d480bb
      This is on linux but on windows there is an equivelent interop call.

    • @pilotboba
      @pilotboba Год назад

      @@nickchapsas ok, must have missed that. Thanks.

  • @chrissimpson6088
    @chrissimpson6088 Год назад

    System.Environment.TickCount is super lightweight

    • @nickchapsas
      @nickchapsas  Год назад +1

      It's also useless because it only counts milliseconds since you starting your computer. If you care about microseconds or nanoseconds, it can't do anything