Проблемы с производительностью - большие библиотеки DLL и большие пространства имен - PullRequest
1 голос
/ 05 августа 2010

Это своего рода следующий шаг в вопросе LINQ to DB2, который я задал здесь .

После ответа zb_z я немного потрудился скод для DB_Linq и сумели добавить рабочую поддержку DB2.(Он все еще находится в зачаточном состоянии, пока не готов участвовать в проекте.) Доказательство концепции работало отлично, на самом деле это было довольно захватывающе.Однако на этом пути я столкнулся с еще одной ошибкой.

Как оказалось, наша база данных DB2 имеет размер big .8 306 столов большие.Таким образом, сгенерированный код оказался более 5,2 миллиона строк кода.В одном файле.Само собой разумеется, Visual Studio не очень заботился об этом:)

Поэтому я дополнительно изменил генератор, чтобы выкладывать каждый класс таблицы в отдельный файл.В результате у меня осталось 8307 файлов (контекст данных и по одному для каждой таблицы, которые расширяют контекст данных свойствами таблицы).Понятно, что Visual Studio все еще это не нравилось, поэтому я завернул генерацию и компиляцию кода в сценарии и просто запустил его, чтобы вывести DLL для своих проектов.

A 36 МБ DLL.

Теперь, поиск вокруг производительности немного привел меня к этому ТАКому вопросу (который сам по себе ссылается на этот ), и я проследил за ответами и ссылками и посмотрел, чтоони говорят.Так что это заставляет меня задуматься о том, что, возможно, наличие более 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();
        }

Может быть, я упускаю что-то очевидное или что-то в LINQ я не понял?

РЕДАКТИРОВАТЬ

После обсуждения с Джоном и Брайаном ниже я углубился в код DB_Linq, который вызывается при создании запроса 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 ]

2 голосов
/ 05 августа 2010

Я только что создал небольшой тестовый проект с 10.000 классов в пространстве имен, и, хотя при загрузке / сборке сборки возникают заметные накладные расходы, я бы не сказал, что это особенно медленно.Так что, вероятно, не количество классов само по себе является причиной плохой производительности, которую вы видите.

Я Джон, здесь было бы полезно узнать больше информации о вашем тестовом приложении.

2 голосов
/ 05 августа 2010

Размещение консольного приложения действительно помогло бы.

Наличие большого количества классов в пространстве имен и в сборке приведет к замедлению компиляции , и для каждого JITting будет одноразовое нажатиеметод в каждом типе ... но я бы не ожидал, что он замедлит LINQ-запросы.

Вы должны проверить, какой SQL на самом деле генерируется из ваших LINQ-запросов.Я ожидаю, что проблема будет лежать там.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...