Successfully reported this slideshow.
Your SlideShare is downloading. ×

Pragmatic Performance from NDC Oslo 2019

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 32 Ad
Advertisement

More Related Content

Similar to Pragmatic Performance from NDC Oslo 2019 (20)

Advertisement

Pragmatic Performance from NDC Oslo 2019

  1. 1. NDC { Oslo } 2019 David Wengier Microsoft @davidwengier
  2. 2. @davidwengier
  3. 3. - Donald Knuth @davidwengier
  4. 4. - Donald Knuth @davidwengier
  5. 5. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%” - Donald Knuth @davidwengier
  6. 6. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%” - Donald Knuth “Structured Programming with Go To Statements”, 1974@davidwengier
  7. 7. @davidwengier
  8. 8. @davidwengier
  9. 9. @davidwengier
  10. 10. “Don’t do that, that’s slow, and uses too much memory!” @davidwengier
  11. 11. public static void Profile(Action func) { DateTime start = DateTime.Now; for (int i = 0; i < 100; i++) { func(); } Console.WriteLine(“Avg Time Elapsed {0} ms", (DateTime.Now - start).TotalMilliseconds / 100); }
  12. 12. public static void Profile(Action func) { Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; Thread.CurrentThread.Priority = ThreadPriority.Highest; func(); Stopwatch watch = new Stopwatch(); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); watch.Start(); for (int i = 0; i < 100; i++) { func(); } watch.Stop(); Console.WriteLine(“Avg Time Elapsed {0} ms", watch.Elapsed.TotalMilliseconds / 100); }
  13. 13. https://benchmarkdotnet.org/ https://github.com/dotnet/BenchmarkDotNet • • • • @davidwengier
  14. 14. • • • • • • @davidwengier
  15. 15. • • @davidwengier
  16. 16. • • • • • • • • •
  17. 17. 0 bytes allocated 32 bytes allocated
  18. 18. • •
  19. 19. 0 bytes 20 bytes 32 bytes
  20. 20. • •
  21. 21. • •
  22. 22. public class HybridDictionary: IDictionary { // These numbers have been carefully tested to be optimal. // Please don't change them without doing thorough performance // testing. private const int CutoverPoint = 9;
  23. 23. • Should you always use Dictionary<K,V> instead of IDictionary<K,V>? • Should you always use structs instead of classes? • Should you always use for instead of foreach? • Should you always use StringBuilder instead of concatenation? • Should you always use traditional loops instead of Linq? • Should you always avoid throwing exceptions?
  24. 24. • Should you always use Dictionary<K,V> instead of IDictionary<K,V>? No, it depends. • Should you always use structs instead of classes? No, it depends. • Should you always use for instead of foreach? No, it depends. • Should you always use StringBuilder instead of concatenation? No, it depends. • Should you always use traditional loops instead of Linq? No, it depends. • Should you always avoid throwing exceptions? No, it depends. • Should you always … anything? No, it probably depends.
  25. 25. @davidwengier

Editor's Notes

  • Whos heard this?
    This gets carted out on PRs, and usually as a negative. Either someone changes a linq statement to foreach statement, and someone else says “don’t bother, premature opt…”.
    Or someone writes a linq statement, someone comments saying it should be a foreach statement, and then in order to be lazy, “premature opt..”

    But first, Who said it?
  • Donald Knuth. K-nooth. You may notice the formatting is odd.. Well, that’s because its part of a bigger quote
  • Most people don’t know this. 97% of the time its evil, the rest its not. But why?
    Notice the quotes.
  • Consider maintenance and debugging. Readability. Etc.
    Bonus points, what paper was it from?
  • Ironically, a quote that lots of developers spout as being gospel comes from a paper arguing that programming with go to statements is perfectly fine. A debate for another time.
  • How much? How often?
  • Questions assumptions: “the compiler will fix this”. Will it?
    Release builds only!
  • Measure. Before and After.
  • DateTime is as precise as stopwatch, but not as granular
    Doesn’t account for JITing
  • High affinity, so nothing else on the machine matters (hopefully)
    JIT first
    Get GC out of the way, because GC pauses are bad for benchmarking
    Then go
  • Or use BenchmarkDotNet
  • So I’ve told you how to benchmark, and that you should basically never do it, so lets do it.
  • Except its too slow. Its _thorough_ BenchmarkDotNet is too awesome for live demos. I have some canned ones.
  • Concat wins
    Then stringbuilder
    StringBuilder again
    Now StringBuilder with capacity, and that trend continues forever
    If we add more, we can see stringbuilder and string join are both a lot fast
    As we keep going, the difference gets bigger
    Looks like a slam dunk right? Always use a string builder with capacity?
    BUT- don’t forget context!!! Look what happens as the strings get longer. Now it doesn’t matter so much, as long as you don’t concatenate
  • Back to context, again production code.. For 3 small strings, concatenation is slower, but not measurably so. But it uses less memory. The overhead of StringBuilder might make it a winner. For large strings, not so.

    Stopwatch granularity is around 300ns, so the difference in the first one is effectively unmeasurable in production .NET code.

    For large strings SB still uses heaps of memory. Are strings the best thing to use at all? Step back
  • The overhead of a class vs struct, virtual calls, etc. makes the difference.
    So if you use Idictionary as your parameters are you’re supposed to do, on a hot path that might be a negative.
    Question assumptions.. How many of you think “coding to interfaces” is _always_ better?
  • I’m an old man so I like loops, but Linq has an overhead.
  • Overhead in method invocations and allocations. Even the Where vs FirstOrDefault examples, because there are more iterators being allocated. Where uses an iterator, FirstOrDefault uses a foreach loop
  • 50% chance of having an invalid number. The important thing is that both benchmarks will use exactly the same input.
  • Question assumptions!
  • Culture of performance means measure, not necessarily write complicated code
  • Pragmatic. Every decision is unique. Don’t write off performance, but don’t write off anything.

×