性能问题 - 大型DLL和大型命名空间

时间:2010-08-05 19:27:53

标签: .net linq performance dll db2

这是我问here问题的LINQ to DB2问题的下一步。

zb_z的回答之后,我对DB_Linq的代码进行了一些讨论,并设法添加了有效的DB2支持。 (它现在还处于起步阶段,尚未准备好回馈到项目中。)概念验证工作得很好,实际上非常令人兴奋。但是,我一路上遇到了另一次打嗝。

事实证明,我们的DB2数据库。 8,306桌大。因此,生成的代码竟然超过了520万行代码。在一个文件中。毋庸置疑,Visual Studio并不关心它:)

所以我进一步修改了生成器,将每个表类吐出到自己的文件中。这给我留下了8,307个文件(数据上下文和每个表一个,它们使用表属性扩展数据上下文)。 Visual Studio仍然不喜欢它,这是可以理解的,所以我将代码生成和编译包装在一个脚本中,然后运行它来为我的项目输出一个DLL。

一个36 MB的DLL。

现在,搜索一下性能导致我this SO question(它本身引用this one)并且我已经按照答案和链接查看他们在说什么。所以这让我想知道它是否可能在同一名称空间中存在超过8,000个类,这是明显的性能问题的罪魁祸首。

我的性能测试是编写一个初始化数据上下文的小型控制台应用程序,使用LINQ获取数据,打印行数,使用经典ADO获取数据,并打印出另一行计数。每个声明都包含一个时间戳。添加更多查询以进行测试等始终会产生相同的性能。 LINQ代码需要几秒钟才能运行,而ADO会在眨眼之间填充数据集。

所以我猜这最终会成为一个有点开放(并且啰嗦,抱歉)的问题。有没有人对加快绩效有任何想法?我可以应用哪些简单的调整或设计考虑因素?

修改

有几点需要注意:

  1. 如果我将代码生成限制为表的一个子集(例如,200),那么它会更快地运行
  2. 在调试器中单步执行,时间长度花在var foo = from t in bank1.TMX9800F where t.T9ADDEP > 0 select t.T9ADDEP行上,当我在调试器中展开属性以枚举结果时(或者让它转到执行.Count的下一行) ())那部分根本没有时间。
  3. 修改

    我无法发布整个生成的DLL,但这是测试应用程序的代码:

    static void Main(string[] args)
            {
                Console.WriteLine(string.Format("{0}: Process Started", DateTime.Now.ToLongTimeString()));
    
                // Initialize your data contexts
                var bank1 = new BNKPRD01(new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString));
                var bank6 = new BNKPRD06(new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString));
                Console.WriteLine(string.Format("{0}: Data contexts initialized", DateTime.Now.ToLongTimeString()));
    
                var foo = from t in bank1.TMX9800F where t.T9ADDEP > 0 select t; // <- runs slow
                Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD01 test table", DateTime.Now.ToLongTimeString(), foo.Count().ToString()));
    
                var baz = from t in bank6.TMX9800F where t.T9ADDEP > 0 select t; // <- runs slow
                Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD06 test table", DateTime.Now.ToLongTimeString(), baz.Count().ToString()));
    
                var ds = new DataSet();
                using (var conn = new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString))
                {
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = "SELECT * FROM BNKPRD01.TMX9800F WHERE T9ADDEP > 0";
                        new IBM.Data.DB2.iSeries.iDB2DataAdapter(cmd).Fill(ds);
                    }
                }
                Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD01 test table", DateTime.Now.ToLongTimeString(), ds.Tables[0].Rows.Count.ToString()));
    
                ds = new DataSet();
                using (var conn = new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString))
                {
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = "SELECT * FROM BNKPRD06.TMX9800F WHERE T9ADDEP > 0";
                        new IBM.Data.DB2.iSeries.iDB2DataAdapter(cmd).Fill(ds);
                    }
                }
                Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD06 test table", DateTime.Now.ToLongTimeString(), ds.Tables[0].Rows.Count.ToString()));
    
                Console.WriteLine("Press return to exit.");
                Console.ReadLine();
            }
    

    也许我错过了一些显而易见的事情,或者有一些关于LINQ的事情我没有理解?

    修改

    在下面与Jon和Brian讨论后,我进一步研究了在创建LINQ查询时调用的DB_Linq代码,并且遇到了很长的步骤:

    public override IEnumerable<MetaTable> GetTables()
            {
                const BindingFlags scope = BindingFlags.GetField |
                    BindingFlags.GetProperty | BindingFlags.Static |
                    BindingFlags.Instance | BindingFlags.NonPublic |
                    BindingFlags.Public;
                var seen = new HashSet<Type>();
                foreach (var info in _ContextType.GetMembers(scope))
                {
                    // Only look for Fields & Properties.
                    if (info.MemberType != MemberTypes.Field && info.MemberType != MemberTypes.Property)
                        continue;
                    Type memberType = info.GetMemberType();
    
                    if (memberType == null || !memberType.IsGenericType ||
                            memberType.GetGenericTypeDefinition() != typeof(Table<>))
                        continue;
                    var tableType = memberType.GetGenericArguments()[0];
                    if (tableType.IsGenericParameter)
                        continue;
                    if (seen.Contains(tableType))
                        continue;
                    seen.Add(tableType);
    
                    MetaTable metaTable;
                    if (_Tables.TryGetValue(tableType, out metaTable))
                      yield return metaTable;
                    else
                      yield return AddTableType(tableType);
                }
            }
    

    该循环迭代16,718次。

2 个答案:

答案 0 :(得分:2)

发布控制台应用程序确实会有所帮助。

在命名空间和程序集中有很多类会减慢编译,并且每种类型的每个方法都会有一次性的JITting命中...但我不会指望它可以减慢LINQ查询速度。

您应该检查从LINQ查询实际生成的SQL。我希望问题出在那里。

答案 1 :(得分:2)

我刚刚在命名空间中创建了一个带有10.000个类的小型测试项目,虽然在加载/ jitting组件时有明显的开销,但我不会说它特别慢。因此,可能不是类本身的数量,这是你所看到的糟糕表现的原因。

我是乔恩在这里,有关您的测试应用的更多信息会有所帮助。