3

I had a question regarding the performance of having a class implement multiple interfaces. Is there any degradation in having a class implement 2 interfaces vs 10 interfaces?

Background

This question comes from investigating Microsoft's Identity2. I noticed that there were ~6 replicated DB calls during the ClaimsIdentityFactory.CreateIdentity() call. Upon further investigation, it seemed like the code was fetching the user's identity multiple times when checking for SecurityStamp, Roles, and some other info.

This is mainly because the UserStore is supposed to handle SecurityStamp by implementing ISecurityStore rather than it be tied to the TUser or IUser model. While I understand why, couldn't they not just check if the TUser implemented a ISecurityUser and just grab that data. Especially if it just retrieved the TUser a few lines earlier.

It may not be that big of a deal, but it comes into play as I have a JS Script that GETs my API for updates every 10 sec. As the server I am running this one is not that powerfull since it runs many different programs, I am trying to decrease the number of times the program waits for redundant DB calls and releases precious CPU time. Currently it takes ~ 0.02 secs for the call to occur with ~10 DB calls. But there are 5 DB calls that I feel are redundant and removing can further increase the throughput of my code.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
FDaniels
  • 59
  • 7
  • Possible duplicate of [Is micro-optimisation important when coding?](https://softwareengineering.stackexchange.com/questions/99445/is-micro-optimisation-important-when-coding) – gnat Aug 25 '17 at 18:37
  • @gnat see edit for why it is not – FDaniels Aug 25 '17 at 18:41
  • 2
    The amount of time it takes your code to get through those interfaces is at least *three orders of magnitude smaller* than the DB calls. – Robert Harvey Aug 25 '17 at 18:51
  • @RobertHarvey That is certainly true, but that is kinda besides the point. DB calls are relatively larger especially if using C#'s await functionality where it has to then wait for it the response and to get back CPU control. – FDaniels Aug 25 '17 at 18:57
  • 6
    My point is that it is the DB call that is slowing things down, not the interface. Any delay imposed by the interface is almost certainly going to be insignificant compared to the delay imposed by the DB call. – Robert Harvey Aug 25 '17 at 19:00
  • @RobertHarvey This thinking is what brought up the question in the first place. If it was my choice, I would have done an if(user is ISecurityUser){ ... } rather than only going through the store. Or even better, pass the User object to the store rather than just UserID and have it check since that's it's role. Maybe I'll fork it and make the change and see. – FDaniels Aug 25 '17 at 19:05
  • 2
    You might be interested in reading [Why should casts be avoided?](https://stackoverflow.com/a/4168672) and similar posts. There's a strong consensus that manually casting/checking for a type is not a good idea. – amon Aug 25 '17 at 19:12
  • 1
    @amon is right, this is to the point that if I was forced to deal with an object that implemented two interfaces and I needed to talk through both I'd ask for two different references to that same object that had two different types rather then take one and cast it. Clunky as heck but still better than casting. – candied_orange Aug 25 '17 at 19:16
  • @CandiedOrange It's that clunkyness that kinda urks me. They already do it for the UserStore why not do it for the User as well. What would you say is a better implementation? – FDaniels Aug 25 '17 at 19:23
  • 2
    @FDaniels I haven't looked at the code but the problem you describe makes me think this is a design problem not a performance problem. I see this kind of problem in designs that start with turning a list of nouns into classes and only thinking about how they communicate after the interface is set in stone. It's much better to start with a client and design for it the services it needs. – candied_orange Aug 26 '17 at 16:19

2 Answers2

6

By itself, implementing interfaces has no performance impact.

Calling a method through an interface type might be a bit slower than calling a method through a class type, but modern runtimes such as Microsoft's CLR can eliminate the extra overhead in most cases. (They use an optimization technique called an Inline Cache, which the CLR calls Virtual Stub Dispatch.)

Repeatedly computing the same data has nothing to do with interfaces, and even less with the efficiency of interfaces. That is an unrelated design problem.

But it is true that abstracting through interfaces may not always be an appropriate solution for a design problem. Conceptually, using an interface erases information about the actual object. This is sometimes beneficial as you can use multiple objects interchangeably if they implement the same interface. On the other hand, it becomes more difficult to treat these objects differently, e.g. to query an object for additional capabilities. Casting an object to a different interface is frowned upon because this is not obvious for callers of such a function and not very extensible. In some cases generics are better solution than interfaces, but they seem to have no advantage in the context of the question.

Some problems are just difficult to solve well, so opting for a solid but less efficient solution seems legitimate.

amon
  • 132,749
  • 27
  • 279
  • 375
  • Okay. What doesn't sit right with me however, is that MS uses interfaces to query the UserStore for additional capabilities but not for TUser. Even if we only wanted to have the UserStore handle retrieving the SecurityStamp, why couldn't we pass the User item to UserStore and have the check done there. I guess it's like. you're already doing it to one so why not both – FDaniels Aug 25 '17 at 19:17
  • @FDaniels I am not familiar with the UserStore so I won't try to guess why it was designed that way. But judging from your description, they might have chosen for UserStore to not do that check in order to *separate different concerns*. – amon Aug 25 '17 at 19:28
  • That makes some sense. I can see the fact that someone needs to know how to get the information so we'll have one class that can be where we can determine how to get non-default information. – FDaniels Aug 25 '17 at 19:35
0

Short answer: no. The cost of dynamic dispatch doesn't increase as a class implements more interfaces even if the compiler fails to inline. There is a cost to dynamic dispatch but the cost of doing it doesn't scale algorithmically in any implementation I've ever seen regardless of whether a class implements 1 interface or 100.

That said, I pitched in because I believe you have a design-related issue. When performance is a concern, I actually believe you should invert the popular priority of design and favor a data-oriented mindset first. That begins with deciding how your code represents, accesses, and manipulates data first and foremost. The interfaces come second. That doesn't mean you design poor interfaces that do little more than retrieve and manipulate data and exposes unsightly implementation details, quite the opposite. Instead it just means you model things at the appropriate level of coarseness or granularity while taking into account the most efficient way to store, access, and manipulate data.

For efficiency, you often need to think in a bulkier way, not a granular way (Users, not User, Image, not Pixel). You often can't model the teeniest and simplest objects without painting yourself in a corner where you can no longer optimize much further without making cascading, breaking changes to the interfaces in your system.

As a basic example, a particle system really concerned with efficiency as a huge aspect of its quality wouldn't necessarily expose a scalar Particle object that serves as any more than a handle to be exposed and manipulated directly to thousands of disparate clients in a complex codebase. That would limit the ability to centrally optimize code down to the granular level of a single particle with countless dependencies to the granular Particle object. It wouldn't allow you to favor central optimizations that allow you to do many things with multiple particles at once in parallel or vectorized if all the clients are working with one particle at a time with single-threaded scalar code. It wouldn't allow you to coalesce and interleave the data fields of multiple particles at once for, say, efficient SoA representations when each individual particle's fields have to stored within a scalar Particle object. You're trapped in the inefficient realm of being confined to working with a single AoS particle at once and possibly with memory waste from things like padding and hidden per-object data fields with such a design. So such a system might instead expose a ParticleSystem interface with functions to manipulate many particles at once instead of one particle at a time with a scalar Particle interface.

Similar thing for your case. If you want to eliminate many redundant database queries, your starting point for design should be how to efficiently represent, access, store, and manipulate data in a way such that you perform the minimum amount of queries. I'd actually suggest doing this from a central place in bulk initially. The interfaces come second. When the interfaces come first, then you often end up with that case where the data is represented, accessed, and/or manipulated inefficiently as a result of having so many granular classes that are potentially well-coordinated in terms of interfaces and high-level design but not in terms of data access and storage patterns.

That's fine if efficiency isn't a primary goal. In such a case where efficiency isn't a concern, you can always make the implementation behind your granular interfaces do whatever they need to do in order to function regardless of computational cost. Yet when it is a big concern, you should approach design problems with more of a data-oriented mindset so that you don't find yourself in that overly granular inefficiency trap.