Yes You Can Build a Search Engine in
Yes, You Can Build a Search Engine in. Net! Lessons learned building the world’s largest. Net application Ben Watson Principal Software Engineer, Microsoft Author, Writing High-Performance. NET Code
About me • Ben Watson • @benmwatson • Principal Software Engineer, Microsoft • At Bing since 2008 (before it was Bing)
Capability
A Story in Three Acts
Architecture v. Prev
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Rearchitecting
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Dev. Ops Sports Dev. Ops Ads Dev. Ops Speller Dev. Ops Multimedia Dev. Ops Aggregator Dev. Ops News Dev. Ops Finance Dev. Ops Query Understanding Dev. Ops Commerce Dev. Ops Ranker Dev. Ops
Web Dev. Ops Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Web Sports Ads Multimedia Speller Aggregator News Finance Query Understanding Commerce Ranker
Sports Web Ads Multime dia Service News Service Query Understa nding Service Ranker Service Commer ce Service Finance Service Speller Service Aggregat or Service Service Service Service Service
Difficult to accomplish in C++…
Enter: . Net!
“You can’t do that. ”
“C++ is better. C++ is what I know. ” – Senior Management
“. Net is slow. ”
“. Net is slow” fears Myth Code Execution is Slow Reality • JITted code is machine code • Runs natively, not virtually • JITted code has better locality • Compiler is always improving • Hardware is always improving
“. Net is slow” fears Myth Overhead is Too High Reality • Bounds checking Safe memory access • Garbage collection delete, fast memory allocation, safe pointer access • Type overhead Type safety, rich metadata, reflection, exception handling, debugging, static analysis
You WANT the benefits of that overhead.
Benefits • Memory safety • Type safety • Fast allocations H C U M s i g in g g u • Deb better e d o c ic t a t s r e t t e • B analysis • Code locality Costs • JIT n io t c e ll o C e g a b r a G • e p y t , g in k c e h c s d • Boun checking • Hidden code
Benefits • Memory safety • Type safety • Fast allocations CH better • Debugging is MU e analysis d o c c ti a st r e tt e B • • Code locality • Less down-time ons • No Access Violati • Developer Agility • Robust Libraries Costs • JIT • Garbage Collection pe ty , g in k c e h c s d n u o B • checking • Hidden code • Education
Different costs, not worse costs.
Is Managed vs. Unmanaged performance always equivalent?
Learn where your costs are
“Managed code lets mediocre developers write lots of bad code really fast. ”
Performance Isn’t Free
Become Expert
Performance Myths
Myth 1: “You can’t control GC. ”
It’s very easy to build a system that spends an extraordinary amount of time in GC. This is not inherent to the CLR!
You need to understand the inner workings of the GC, just like you would any memory allocator.
Objects must die in a gen 0 GC - or – They must live forever in gen 2.
Pool objects with a long or unknown lifetime.
Pool everything in the large object heap.
The cost of a GC is proportional to the number of surviving objects.
You CAN control the GC
Myth 2: “I’m the Framework Class Library, you can trust me!”
The higher your performance requirements, the less you trust others’ code.
How would you implement Enum. Has. Flag()?
Read the Source
Quiz: How many different XML serialization options ship with. NET?
LINQ-to-XML Xml. Text. Reader Xml. Validating. Reader Data. Contract. Serializer Xml. Serializer XDocument XPath. Navigator
Methods that throw exceptions in normal conditions.
Methods that do more than you need
Activator. Create. Instance public static object Create. Instance(Type type, Binding. Flags binding. Attr, Binder binder, object[] args, Culture. Info culture, object[] activation. Attributes) { if (type == null) { throw new Argument. Null. Exception("type"); } if (type is Type. Builder) { throw new Not. Supported. Exception(Environment. Get. Resource. String("Not. Supported_Create. Instance. With. Type. B uilder")); } if ((binding. Attr & (Binding. Flags)255) == Binding. Flags. Default) { binding. Attr |= (Binding. Flags. Instance | Binding. Flags. Public | Binding. Flags. Create. Instance); } if (activation. Attributes != null && activation. Attributes. Length != 0) { if (!type. Is. Marshal. By. Ref) { throw new Not. Supported. Exception(Environment. Get. Resource. String("Not. Supported_Activ. Attr. On. Non. MBR")); } if (!type. Is. Contextful && (activation. Attributes. Length > 1 || !(activation. Attributes[0] is Url. Attribute))) { throw new Not. Supported. Exception(Environment. Get. Resource. String("Not. Supported_Non. Url. Attr. On. MBR")); } } Runtime. Type runtime. Type = type. Underlying. System. Type as Runtime. Type; if (runtime. Type == null) { throw new Argument. Exception(Environment. Get. Resource. String("Arg_Must. Be. Type"), "type"); } Stack. Crawl. Mark stack. Crawl. Mark = Stack. Crawl. Mark. Look. For. My. Caller; return runtime. Type. Create. Instance. Impl(binding. Attr, binder, args, culture, activation. Attributes, ref stack. Crawl. Mark); } // System. Runtime. Type [Security. Critical] internal object Create. Instance. Impl(Binding. Flags binding. Attr, Binder binder, object[] args, Culture. Info culture, object[] activation. Attributes, ref Stack. Crawl. Mark stack. Mark) { this. Create. Instance. Check. This(); object result = null; try { try { if (activation. Attributes != null) { Activation. Services. Push. Activation. Attributes(this, activation. Attributes); } if (args == null) { args = Empty. Array<object>. Value; } int num = args. Length; if (binder == null) { binder = Type. Default. Binder; } if (num == 0 && (binding. Attr & Binding. Flags. Public) != Binding. Flags. Default && (binding. Attr & Binding. Flags. Instance) != Binding. Flags. Default && (this. Is. Generic. COMObject. Impl() || base. Is. Value. Type)) { result = this. Create. Instance. Default. Ctor((binding. Attr & Binding. Flags. Non. Public) == Binding. Flags. Default, false, true, ref stack. Mark); } else { Constructor. Info[] constructors = this. Get. Constructors(binding. Attr); List<Method. Base> list = new List<Method. Base>(constructors. Length); Type[] array = new Type[num]; for (int i = 0; i < num; i++) { if (args[i] != null) { array[i] = args[i]. Get. Type(); } for (int j = 0; j < constructors. Length; j++) { if (Runtime. Type. Filter. Apply. Constructor. Info((Runtime. Constructor. Info)constructors[j], binding. Attr, Calling. Conventions. Any, array)) { list. Add(constructors[j]); } Method. Base[] array 2 = new Method. Base[list. Count]; list. Copy. To(array 2); if (array 2 != null && array 2. Length == 0) { array 2 = null; } if (array 2 == null) { if (activation. Attributes != null) { Activation. Services. Pop. Activation. Attributes(this); activation. Attributes = null; } throw new Missing. Method. Exception(Environment. Get. Resource. String("Missing. Constructor_Name", new object[] { this. Full. Name })); } object obj = null; Method. Base method. Base; try { method. Base = binder. Bind. To. Method(binding. Attr, array 2, ref args, null, culture, null, out obj); } catch (Missing. Method. Exception) { method. Base = null; } if (method. Base == null) { if (activation. Attributes != null) { Activation. Services. Pop. Activation. Attributes(this); activation. Attributes = null; } throw new Missing. Method. Exception(Environment. Get. Resource. String("Missing. Constructor_Name", new object[] { this. Full. Name })); } if (Runtime. Type. Delegate. Type. Is. Assignable. From(method. Base. Declaring. Type)) { new Security. Permission(Security. Permission. Flag. Unmanaged. Code). Demand(); } if (method. Base. Get. Parameters. No. Copy(). Length == 0) { if (args. Length != 0) { throw new Not. Supported. Exception(string. Format(Culture. Info. Current. Culture, Environment. Get. Resource. String("Not. Supported_Call. To. Var. Arg"), Array. Empty<object>())); } result = Activator. Create. Instance(this, true); } else { result = ((Constructor. Info)method. Base). Invoke(binding. Attr, binder, args, culture); if (obj != null) { binder. Reorder. Argument. Array(ref args, obj); } } finally { if (activation. Attributes != null) { Activation. Services. Pop. Activation. Attributes(this); activation. Attributes = null; } } catch (Exception) { throw; } return result;
Generate code dynamically!
Myth 3: “Premature optimization is the root of all evil. ”
Understand the performance environment before you start.
Results
Latency: 50% Reduction
Stability
Time to Debug
Availability
Team Performance
Sports Web Ads Multime dia Service News Service Query Understa nding Service Ranker Service Commer ce Service Finance Service Speller Service Aggregat or Service Service Service Service Service
• 1: 40 pm - Performance Mythbusting Panel – Bayview AB • 2: 55 pm -. NET AMA w/ Mads Torgersen & Ben Watson – Waterfront CDE
- Slides: 71