none
Performance Issues RRS feed

  • Question

  • We have a requirement to create an extension for Visual Studio 2010. The package is a WPF window which should present the list of classes from the selected projects in a hierarchical tree. When a tree node is selected, the properties of the class should be presented in a grid. User should be able to add additional nodes (classes) and properties using the WPF interface.  The appropriate source code should be updated based on the user action. Currently, we are leveraging the APIs available in Visual Studio 2010 SDK for the above scenario. We are experiencing severe performance issues when there are 1000 files with 40000 properties. The performance issue is noticeable event with few files with 4000 properties. We are not sure why Visual Studio supplied APIs does not yield the desired performance. We are considering following options and we would like to get your opinion. We understand that Visual Studio is COM based application, which we manipulate from the .NET add-in. Is this the expected behavior?

    We understand that Visual Studio is COM based application, which we manipulate from the .NET add-in.

    We are also considering following options and we would like to get your opinion.

    Solution 1:

    1. Use SQL Server database to store the required meta data to load the class names in to the tree view. When a node is clicked, the properties for the corresponding class will be presented using the meta data stored in database.
      2. When user adds new node or properties, use DXcore to add new classes or properties.

    Solution 2:

    Use just the DXCore framework without having to depend on meta data stored in SQL Server database to populate the tree, properties, and update the source code.

    Since this is business critical requirement, we would like to make the right decision. Can you please review the above approaches and direct us in the right path. Sincerely appreciate your assistance.

    Thursday, April 14, 2011 1:53 PM

Answers

  • I am not familar with the code model stuff, but it may be something wherein DTE relies on private interfaces under the covers :(  Each language team provides their own implementation of it so it may be the case there is no 'unified' overlay other than DTE.

    If you are using the ClassView as an example the following information may be relevant in terms of comparison to what you are seeing in your app:

    1:  The ClassView is entirely written in native C++, by a number of developers over a long(ish) period of time, it is heavily optmized to be fast at what it does.

    2:  The ClassView does not use the code model stuff at all from what I can see. It may be because it was written before that existed or perhaps because they couldn't get the perf they needed out of what was available.  It appears to use IVsObjectManager and IVsObjectList for a lot of what it does, but I have no experience with either of those interfaces or with the class view itself so I am not sure how to use that to emulate what the code model stuff is doing.

    Ryan

    • Marked as answer by Victor_Chen Wednesday, April 27, 2011 8:08 AM
    Friday, April 15, 2011 5:36 PM
    Moderator

All replies

  • This isn't nearly enough information to give any useful advice.

    1: Are you creating a package or an AddIn?

    2: Are you using raw VS service interfaces or an abstraction layer like MPF and/or DTE?

    3: Have you profiled your application to determine where the bottlenecks are?

    Ryan

    Thursday, April 14, 2011 2:18 PM
    Moderator
  • Thanks Ryan. Please see below the answers:

    1: Are you creating a package or an AddIn?

    >> Package

    2: Are you using raw VS service interfaces or an abstraction layer like MPF and/or DTE?

    >> We are using DTE

    3: Have you profiled your application to determine where the bottlenecks are?

    >> Yes.The issue appears to be related to calls to the  unmanaged code related to DTE

    Thursday, April 14, 2011 4:37 PM
  • DTE is not and never was intended for performance, it was originally intended to expose VS services to scripting languages like VBScript. It is a wrapper layer over the top of the real VS services and it can exhibit performance issues as performance was never one of its goals and it is an abstraction layer (the old saying 'all performance problems can be solved by removing a layer of indirection' is appropriate here). 

    I would suggest, as a first step, eliminating your use of DTE.

    Ryan

    Thursday, April 14, 2011 4:52 PM
    Moderator
  • Thanks Ryan. We are new to dealing this type of requirement. Can you please point us to alternatives to DTE for our scenario? Sincerely appreciate your help.

    Thursday, April 14, 2011 5:46 PM
  • It is impossible to say without knowing how you are using DTE.  Everything DTE does is accomplished through using various IVsXXX type interfaces, alternatively it may also be available via MPF which is a more modern abstraction layer intended for use in managed code and less...perf challenged at times.

    In geenral if perf is your goal you want as few layers between you and VS as possible, otherwise you have stack frames and code running that simply translates from your input to what the VS interfaces want.  Sometimes that translation is helpful as it is complicated, other times it just basically passes the values along directly or with very minor changes so you pay performance penalties in time and space for very little benefit.

    Ryan

    Thursday, April 14, 2011 6:48 PM
    Moderator
  • Thanks again. The following code snippet shows how we use the DTE to get the soluiton projects. 

    blic static DTE2 DTE
    {
    get
    {

    return (ServiceProvider.GlobalProvider.GetService(typeof(SDTE)) as DTE2);
    }

     

    public void Test()
    {

    EnvDTE.Solution solution = DTE.Solution;
    //EnvDTE.Solution solution = CodeRush.ApplicationObject.Solution;
    EnvDTE.Projects __projects = solution.Projects; 

    }

     

    Thanks

    Rasheed

    Thursday, April 14, 2011 7:39 PM
  • The replacement for that would be IVsSolution::GetProjectEnum

    IVsSolution solution = (IVsSolution)ServiceProvider.GlobalProvider.GetService(typeof(SVsSolution));

    Guid ignored = Guid.Empty;
    IEnumHierarchies hierarchyEnum;
    ErrorHandler.ThrowOnFailure(solution.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref ignored, out hierarchyEnum));

    That said you said that profiling had revealed bottlenecks 'in DTE'.  DTE has a HUGE surface area, what you have above is a single use of a couple of DTE objects (the Solution object and the Projects collection).  Rewriting EVERYTHING to not use DTE may be a huge undertaking, it would be better to look for the bits that are giving the problems and rewrite just enough to eliminate those.

    That said, there are also likely lots of optimizations you can make on your WPF side.  Specifically the WPF tree view does not virtualize and thus having 4000 children is probably likely to be terrible regardless of anything else you may do.

    It sounds like your clients want you to rewrite the Solution Explorer and the Properties Window...to which I can only ask...why?  Those are not small undertakings.

    Ryan

    Thursday, April 14, 2011 7:55 PM
    Moderator
  • Thanks Ryan.  We are building a domain specific IDE for a tax preparation company.  The end product of the IDE allows users to file tax returns electronically (Similar to TurboTax online). The tax forms are design using Visual Studio Design Surface. The data element required to bind to UI controls in the tax forms are designed by the subject matter experts (non-programmers) using the Visual Studio extension package we have created. Every state and federal agencies have its own Dataset to support the different tax forms. The datasets (Visual Studio Project) are organized in a tree. The dataset will have records (Class Files). Records will have number of data elements (properties of a class). The total number of data elements for a record would be close to ~4500. At any given time, only one data set is expected to be designed.

    Please see below the simplified code block on what we are trying to do (we have incorporated your recommendation):

     

    using System;
    
    using System.Collections.Generic;
    
    using System.Text;
    
    using System.Windows;
    
    using System.Windows.Controls;
    
    using EnvDTE;
    
    using Microsoft.VisualStudio.Shell;
    
    using EnvDTE80;
    
    using Microsoft.VisualStudio.Shell.Interop;
    
    
    
    namespace Microsoft.CoreTest
    
    {
    
     /// <summary>
    
     /// Interaction logic for MyControl.xaml
    
     /// </summary>
    
     public partial class MyControl : UserControl
    
     {
    
      private StringBuilder builder = new StringBuilder();
    
    
    
      public MyControl()
    
      {
    
       InitializeComponent();
    
      }
    
    
    
    
    
      private void button1_Click(object sender, RoutedEventArgs e)
    
      {
    
       try
    
       {
    
        ExtractProjects();
    
        tbox.Clear();
    
        tbox.Text = builder.ToString();
    
    
    
       }
    
       catch (Exception ex)
    
       {
    
        tbox.Text = ex.Message;
    
        tbox.AppendText(ex.StackTrace);
    
       }
    
      }
    
    
    
    
    
      public static DTE2 DTE
    
      {
    
       get
    
       {
    
    
    
        return (ServiceProvider.GlobalProvider.GetService(typeof(SDTE)) as DTE2);
    
       }
    
      }
    
    
    
      public static IVsSolution DTEX
    
      {
    
       get
    
       {
    
        return ServiceProvider.GlobalProvider.GetService(typeof(IVsSolution)) as IVsSolution;
    
    
    
       }
    
      }
    
      public void ExtractProjects()
    
      {
    
       List<Project> projects = new List<Project>();
    
       IVsSolution solution = DTEX;
    
       Project project = null;
    
    
    
       IEnumHierarchies ppEnum;
    
       Guid tempGuid = Guid.Empty;
    
       solution.GetProjectEnum((uint)Microsoft.VisualStudio.Shell.Interop.__VSENUMPROJFLAGS.EPF_ALLPROJECTS, ref tempGuid, out ppEnum);
    
    
    
       if (ppEnum != null)
    
       {
    
        uint actualResult = 0;
    
        IVsHierarchy[] nodes = new IVsHierarchy[1];
    
        while (0 == ppEnum.Next(1, nodes, out actualResult))
    
        {
    
         Object obj;
    
         nodes[0].GetProperty((uint)Microsoft.VisualStudio.VSConstants.VSITEMID_ROOT, (int)Microsoft.VisualStudio.Shell.Interop.__VSHPROPID.VSHPROPID_ExtObject, out obj);
    
         project = obj as Project;
    
         if (project != null && project.Name.EndsWith("Data"))
    
         {
    
          break;
    
         }
    
        }
    
       }
    
    
    
       if (null != project)
    
       {
    
    
    
        var items = project.ProjectItems.GetEnumerator();
    
        while (items.MoveNext())
    
        {
    
         ProjectItem curr = items.Current as ProjectItem;
    
         ExtractClasses(curr);
    
        }
    
       }
    
    
    
      }
    
    
    
    
    
      private void ExtractClasses(ProjectItem projectItem)
    
      {
    
       FileCodeModel fileCodeModel = projectItem.FileCodeModel;
    
       if (fileCodeModel != null)
    
       {
    
        var items = fileCodeModel.CodeElements.GetEnumerator();
    
    
    
        while (items.MoveNext())
    
        {
    
         EnvDTE.CodeElement codeElement = items.Current as EnvDTE.CodeElement;
    
    
    
         if (codeElement.Kind == vsCMElement.vsCMElementNamespace || codeElement.Kind == vsCMElement.vsCMElementClass)
    
         {
    
          if (codeElement is CodeNamespace)
    
          {
    
           var cns = (CodeNamespace)codeElement;
    
           CodeClass cl = null;
    
    
    
           if (cns.Members.Count > 0 && (cl = cns.Members.Item(1) as CodeClass) != null)
    
           {
    
            ExtractProperties(cl);
    
           }
    
          }
    
          else if (codeElement is CodeClass)
    
          {
    
           ExtractProperties(codeElement as CodeClass);
    
          }
    
         }
    
    
    
        }
    
    
    
       }
    
    
    
      }
    
    
    
      private void ExtractProperties(CodeClass CodeClass)
    
      {
    
       var items = CodeClass.Children.GetEnumerator();
    
       while (items.MoveNext())
    
       {
    
        var codeProperty = items.Current as CodeProperty;
    
        if (codeProperty != null)
    
        {
    
         builder.AppendLine(codeProperty.Name);
    
        }
    
       }
    
      }
    
    
    
     }
    
    }
    
    

    We are still experiencing same performance issue. It take close to 3 minutes to load all the data elements. Are we doing anything fundamentally wrong here? Please advice. Appreciate your help.

    On a side note, within Visual Studio, if we open the built-in class view explorer, it's pretty fast. We would expect the similar behaviour in our extension package.

    Many thanks

    Rasheed

     


    • Edited by arasheed Friday, April 15, 2011 3:58 PM Format change
    Friday, April 15, 2011 3:57 PM
  • You're still using DTE, ExtObject is just a DTE object. As for being as fast as class explorer it is possible, I mean class explorer exists, but that doesn't mean it would be trivially easy. The easier the code the less likely it is to scale and be performing because you are optimizing for developer convienece (easier to write code) over user convienence (a fast program).

    Ryan


    Friday, April 15, 2011 4:05 PM
    Moderator
  • Thanks Ryan for your patience. Can you please point us to some resources related to non DTE usage scenario? For example, w are struggling identify the non DTE equivalent of the following code

          EnvDTE.CodeElement codeElement = items.Current as EnvDTE.CodeElement

     

    Friday, April 15, 2011 4:59 PM
  • I am not familar with the code model stuff, but it may be something wherein DTE relies on private interfaces under the covers :(  Each language team provides their own implementation of it so it may be the case there is no 'unified' overlay other than DTE.

    If you are using the ClassView as an example the following information may be relevant in terms of comparison to what you are seeing in your app:

    1:  The ClassView is entirely written in native C++, by a number of developers over a long(ish) period of time, it is heavily optmized to be fast at what it does.

    2:  The ClassView does not use the code model stuff at all from what I can see. It may be because it was written before that existed or perhaps because they couldn't get the perf they needed out of what was available.  It appears to use IVsObjectManager and IVsObjectList for a lot of what it does, but I have no experience with either of those interfaces or with the class view itself so I am not sure how to use that to emulate what the code model stuff is doing.

    Ryan

    • Marked as answer by Victor_Chen Wednesday, April 27, 2011 8:08 AM
    Friday, April 15, 2011 5:36 PM
    Moderator
  • Ryan - Thank you very much!
    Sunday, May 8, 2011 7:57 PM