【问题标题】:Performance Problems - Large DLLs and Large Namespaces性能问题 - 大型 DLL 和大型命名空间
【发布时间】:2010-08-05 19:27:52
【问题描述】:

这是我向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() 的下一行) 那么这部分根本不需要时间。

编辑

我无法发布整个生成的 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 次。

【问题讨论】:

  • 做 BNKPRD??类将所有表枚举为字段/属性?
  • @Brian:我认为你正在做一些事情。我只是在调试器中进一步进入 DB_Linq 代码,并遇到了一个我将添加到问题中的方法......
  • 嗯,到目前为止,我还不能重现类似的东西(但我保持简单,所以在我的情况下没有 LINQ 提供程序)。您添加的代码确实使用了反射,这可能会带来很大的开销。您是否尝试过分析应用程序?尝试使用例如分析它200、1000 和 2000 表,看看会弹出什么数字。
  • @Brian:绝对是 GetTables() 方法中的迭代。我可能会好好研究一下,看看我是否可以调整它以满足我在这个项目中的需求。如果我可以在该方法调用中及其周围获得更多上下文,我可能能够获得我需要的确切成员而无需迭代。无论哪种方式,都会很有趣。

标签: .net linq performance dll db2


【解决方案1】:

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

我是 Jon,如果您提供有关您的测试应用的更多信息,将会很有帮助。

【讨论】:

  • 我认为是您上面的评论使天平倾斜,所以我会给您答案。在这一点上,我们已经超越了最初的问题(你和 Jon 都有效地回答了这个问题),所以如果我在开发过程中遇到另一个潜在的障碍,我会提出一个新问题。谢谢!
【解决方案2】:

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

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

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

【讨论】:

  • 我也这么认为,但如果我将代码生成限制为表的一个子集(例如,其中 200 个),那么它的运行速度会快得多。我会看看我是否可以获取生成的 SQL 并用它更新问题...
  • 当我意识到我将公开发布金融机构核心数据库的整个结构时,我正在寻找发布申请的地方。我们可以整天争论这是否重要,但最终这不是我们的决定:(
  • @David:啊,当你有一个表的子集时获得速度提升的意义可能非常重要。一定要仔细看看 SQL,即使你不能发布它。
  • @David:这些表的互连程度如何?您是否尝试过使用预编译查询? (这样第一次你可能仍然会受到很大的打击,但第二次会更快。)
  • 据我所知,数据库内部的结构非常平坦。他们没有过多地使用(如果有的话)外键和约束。因此,在针对 DB2 生成时,我刚刚将其排除在代码生成步骤之外。虽然当我们谈论表格子集上的差异时,确实有一些事情让我印象深刻。我现在认为延迟很可能是在生成 SQL 的 DB_Linq 库中。我必须将该代码连接到调试器,并更深入地了解那个长步骤在哪里。
猜你喜欢
  • 2021-11-28
  • 1970-01-01
  • 2011-06-03
  • 1970-01-01
  • 2016-11-04
  • 2013-05-09
  • 2019-06-29
  • 2022-12-31
  • 2012-04-24
相关资源
最近更新 更多