safe systems programming
play

Safe Systems Programming in C# and .NET Joe Duffy - PowerPoint PPT Presentation

Safe Systems Programming in C# and .NET Joe Duffy joeduffyblog.com @xjoeduffyx joeduffy@acm.org Introduction Systems? Anywhere youre apt to think about bits, bytes, instructions, and cycles Demanding performance


  1. Safe Systems Programming in C# and .NET Joe Duffy joeduffyblog.com · @xjoeduffyx · joeduffy@acm.org

  2. Introduction

  3. “Systems”? • Anywhere you’re apt to think about “bits, bytes, instructions, and cycles” • Demanding performance and reliability requirements • Classically meant “interacts with hardware,” requiring C and unsafe code • Abstraction has shifted upwards thanks to concurrency and cloud; security is more important now than ever • Many scenarios: operating systems, drivers, tools, libraries, cloud infrastructure, web servers, micro services and their frameworks, …

  4. Why C#? • Productivity and ease of use • Built-in safety: fewer inherent security and reliability risks • Powerful async and concurrency models • Momentum, mature ecosystem of tools and libraries

  5. Why Not C#? • Garbage collection • Allocation-rich APIs and patterns • Error model that makes reliability challenging • Concurrency is based on unsafe multithreading

  6. This Talk • We are making progress on all of the above • New C# and library features • Patterns that you can apply today, enforce w/ Roslyn Analyzers • Advances in code generation technologies • All open source, on GitHub, and developed with the community

  7. Performance

  8. Code Generation • A spectrum of code generation Just In Time • JIT: fast compile times, simple, decent code quality • AOT: best code quality, slower compile times, more complex deployment model • Hybrid: pick and choose, let the system Ahead of Time adaptively recompile (future)

  9. Code Generation R2R x86 RyuJIT JIT C# AOT IL Compiler x64 AOT LLILC ARM32 LLVM LIR ARM64 CoreCLR CoreRT Windows Mac OS X Linux …

  10. Optimizations • Inlining • SSA and global value numbering • Flowgraph and loop analysis • Common subexpression elimination • Copy/constant propagation • Dead code elimination • Range analysis • Devirtualization • Loop invariant code hoisting • Generic sharing • SIMD and vectorization • Stack allocation (work in progress)

  11. Ex. Inlining int a = …, b = …; Swap<int>(ref a, ref b); static void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } Calls are not cheap: new stack frame, save return address, save registers that might be overwritten, call, restore Adds a lot of waste to leaf-level functions (10s of cycles)

  12. Ex. Inlining int a = …, b = …; int tmp = a; a = b; b = tmp;

  13. Ex. Range Analysis int[] elems = …; for (int i = 0; i < elems.Length ; i++) { elems[i] ++; } Loop “obviously” never goes out of bounds But a naive compiler will do an induction check (i < elems.Length) plus a bounds check (elems[i]) on every iteration!

  14. Ex. Range Analysis int[] elems = …; for (int i = 0; i < elems.Length ; i++) { elems[i] ++; ; Initialize induction variable to 0: } 3D45: 33 C0 xor eax,eax ; Put bounds into EDX: 3D58: 8B 51 08 mov edx,dword ptr [rcx+8] ; Check that EAX is still within bounds; jump if not: 3D5B: 3B C2 cmp eax,edx 3D5D: 73 13 jae 3D72 ; Compute the element address and store into it: 3D5F: 48 63 D0 movsxd rdx,eax 3D62: 89 44 91 10 mov dword ptr [rcx+rdx*4+10h],eax ; Increment the loop induction variable: 3D66: FF C0 inc eax ; If still in bounds, then jump back to the loop beginning: 3D68: 83 F8 64 cmp eax,64h 3D6B: 7C EB jl 3D58 ; ... ; Error routine: 3D72: E8 B9 E2 FF FF call 2030

  15. Ex. Range Analysis int[] elems = …; for (int i = 0; i < elems.Length ; i++) { elems[i]++; ; Initialize induction variable to 0: } 3D95: 33 C0 xor eax,eax ; Compute the element address and store into it: 3D97: 48 63 D0 movsxd rdx,eax 3D9A: 89 04 91 mov dword ptr [rcx+rdx*4],eax ; Increment the loop induction variable: 3D9D: FF C0 inc eax ; If still in bounds, then jump back to the loop beginning: 3D9F: 83 F8 64 cmp eax,64h 3DA2: 7C F3 jl 3D97

  16. Ex. Stack Allocation string name = "Alexander Hamilton"; List<Customer> custs = …; int index = custs.IndexOf(c => c.Name == name); int IndexOf(Func<T, bool> p) { for (int i = 0; i < this.count; i++) { if (p(this[i])) return i; } return -1; }

  17. Ex. Stack Allocation string name = "Alexander Hamilton"; List<Customer> custs = …; int index = custs.IndexOf( c => c.Name == name ); Stack Heap p <lambda> int IndexOf(Func<T, bool> p) { <closure> name for (int i = 0; i < this.count; i++) { if (p(this[i])) <func> return i; } return -1; Allocates up to 2 objects (lambda+captured stack frame) }

  18. Ex. Stack Allocation string name = "Alexander Hamilton"; List<Customer> custs = …; int index = custs.IndexOf(c => c.Name == name); Stack p int IndexOf(Func<T, bool> p) { <lambda> for (int i = 0; i < this.count; i++) { <closure> name if (p(this[i])) return i; <func> } return -1; Allocates up to 2 objects (lambda+captured stack frame) Automatic escape analysis can determine ‘p’ doesn’t escape IndexOf }

  19. Ex. Stack Allocation string name = "Alexander Hamilton"; List<Customer> custs = …; int index = custs.IndexOf(c => c.Name == name); Stack p int IndexOf(Func<T, bool> p) { <lambda> for (int i = 0; i < this.count; i++) { <closure> name if (p(this[i])) return i; <func> } return -1; Allocates up to 2 objects (lambda+captured stack frame) Automatic escape analysis can determine ‘p’ doesn’t escape IndexOf }

  20. Ex. Stack Allocation string name = "Alexander Hamilton"; List<Customer> custs = …; int index = custs.IndexOf(c => c.Name == name); Stack p int IndexOf( [Scoped] Func<T, bool> p) { <lambda> for (int i = 0; i < this.count; i++) { <closure> name if (p(this[i])) return i; <func> } return -1; Allocates up to 2 objects (lambda+captured stack frame) Automatic escape analysis can determine ‘p’ doesn’t escape IndexOf }

  21. Ex. Stack Allocation string name = "Alexander Hamilton"; List<Customer> custs = …; int index = -1; Stack for (int i = 0; i < custs.count; i++) { name if (custs[i].Name == name) { index = i; break; } } Allocates up to 2 objects (lambda+captured stack frame) Automatic escape analysis can determine ‘p’ doesn’t escape IndexOf Best case, IndexOf is inlined, zero allocations, no virtual call!

  22. Memory

  23. CPU, Cache, Memory Latency numbers every programmer should know L1 cache reference ......................... 0.5 ns Branch mispredict ............................ 5 ns L2 cache reference ........................... 7 ns Mutex lock/unlock ........................... 25 ns Main memory reference ...................... 100 ns Compress 1K bytes with Zippy ............. 3,000 ns = 3 µs Send 2K bytes over 1 Gbps network ....... 20,000 ns = 20 µs SSD random read ........................ 150,000 ns = 150 µs Read 1 MB sequentially from memory ..... 250,000 ns = 250 µs Round trip within same datacenter ...... 500,000 ns = 0.5 ms Read 1 MB sequentially from SSD* ..... 1,000,000 ns = 1 ms Disk seek ........................... 10,000,000 ns = 10 ms Read 1 MB sequentially from disk .... 20,000,000 ns = 20 ms Send packet CA->Netherlands->CA .... 150,000,000 ns = 150 ms L1i/d$: 32KB, L2: 256KB, L3: 8MB* Summary: Instructions matter; memory matters more (and I/O dwarfs them all…) * Intel i7 “Haswell” processor cache sizes

  24. CPU, Cache, Memory Latency numbers every programmer should know L1 cache reference ......................... 0.5 ns Branch mispredict ............................ 5 ns L2 cache reference ........................... 7 ns Mutex lock/unlock ........................... 25 ns Main memory reference ...................... 100 ns Compress 1K bytes with Zippy ............. 3,000 ns = 3 µs Send 2K bytes over 1 Gbps network ....... 20,000 ns = 20 µs SSD random read ........................ 150,000 ns = 150 µs Read 1 MB sequentially from memory ..... 250,000 ns = 250 µs Round trip within same datacenter ...... 500,000 ns = 0.5 ms Read 1 MB sequentially from SSD* ..... 1,000,000 ns = 1 ms } GC Pause Disk seek ........................... 10,000,000 ns = 10 ms Read 1 MB sequentially from disk .... 20,000,000 ns = 20 ms Send packet CA->Netherlands->CA .... 150,000,000 ns = 150 ms L1i/d$: 32KB, L2: 256KB, L3: 8MB* Summary: Instructions matter; memory matters more (and I/O dwarfs them all…; and so does GC) * Intel i7 “Haswell” processor cache sizes

  25. Garbage Collection • Generational, compacting garbage collector • Concurrent background scanning for automatically reduced pause times • Manual “low pause” regions (LowLatency) • Parallel collector for server workloads; in .NET 4.5, concurrent+parallel in harmony * https://blogs.msdn.microsoft.com/dotnet/2012/07/20/the-net-framework-4-5-includes-new-garbage-collector-enhancements-for-client-and-server-apps/

Recommend


More recommend