代码之家  ›  专栏  ›  技术社区  ›  David

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

  •  1
  • David  · 技术社区  · 14 年前

    这是我问的Linq to DB2问题的下一步 here .

    跟随 zb_z 答案是,我翻了一下密码 DB_Linq 并设法添加了有效的DB2支持。(现在它还处于初级阶段,还没有准备好为这个项目做出贡献。)概念验证非常有效,实际上非常令人兴奋。不过,我在路上碰到了另一个打嗝。

    事实证明,我们的DB2数据库 大的 . 8306张大桌子。因此,生成的代码超过520万行。在一个文件中。不用说,Visual Studio不太关心它:)

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

    一个36兆字节的DLL。

    现在,搜索一下性能,我发现 this SO question (其本身引用 this one )我跟踪了答案和链接,看看他们在说什么。因此,这让我想知道它是否可能在同一个名称空间中存在超过8000个类,而这正是引起显著性能问题的罪魁祸首。

    我的性能测试是编写一个小控制台应用程序,它初始化数据上下文,用LINQ获取数据,打印行数,用经典ADO获取数据,并打印另一行数。每条语句都包含一个时间戳。向测试中添加更多的查询等总是产生相同的性能。LINQ代码运行需要几秒钟,而ADO则在眨眼间填充数据集。

    所以我想这最终是一个有点开放性的问题(而且冗长的,对此感到抱歉)。有人对加速这里的表演有什么想法吗?有什么简单的调整,或者我可以应用的设计考虑吗?

    编辑

    需要注意的几点:

    1. 如果我将代码生成限制在表的一个子集(比如200)上,那么它将运行 许多的 更快。
    2. 在调试器中单步执行时,所花费的时间长度是 var foo = from t in bank1.TMX9800F where t.T9ADDEP > 0 select t.T9ADDEP 当我在调试器中展开属性来枚举结果(或者让它转到执行.count()的下一行)时,该部分完全不需要时间。

    编辑

    我无法发布整个生成的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();
            }
    

    也许我遗漏了一些明显的东西,或者我没有摸索过的林肯的某些东西?

    编辑

    在下面与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);
                }
            }
    

    这个循环迭代16718次。

    2 回复  |  直到 14 年前
        1
  •  2
  •   Brian Rasmussen    14 年前

    我刚刚在一个名称空间中创建了一个包含10000个类的小型测试项目,虽然在加载/抖动程序集时会有明显的开销,但我不会说它特别慢。因此,您看到的性能不佳的原因可能不是类本身的数量。

    我是乔恩,在你的测试应用程序上提供更多信息会很有帮助。

        2
  •  2
  •   Jon Skeet    14 年前

    发布控制台应用程序真的很有帮助。

    在一个名称空间和一个程序集中有许多类将减慢速度 汇编 每种类型的方法都会有一次抖动。但我不希望它减慢LINQ查询的速度。

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