Кое что про параллельное выполнение кода в Revit API

|  Revit API

Как известно, Revit API не содержит функций для параллельного выполнения кода. Из-за этого складывается ошибочное мнение, что Ревит не может выполнять параллельные задачи. В этой статье я предоставлю макрос для Ревит, который будет содержать некоторые методы, выполняющие параллельные вычисления.

Давно я ничего не писал. Потому что был занят. Был с проверкой в Вильнюсе. Башня Гедемина на месте. И Замковая гора тоже. Ну вы можете наблюдать меня с женой на самой ее вершине. Через несколько часов мы усталые с ужасом смотрели на гору и думали, как же мы смогли влезть на нее по скользкой зимней дороге ) Проверка была завершена удачно.

Ну а теперь к делу. В данной статье просто глянем насколько параллельные вычисления быстрее обычных, выполняемых в главном потоке. В конце статьи и расскажу о нескольких принципах, позволяющих использовать параллельные вычисления вместе с Revit API.

Прежде всего нужна серьезная задача для наших тестов. Возьмем книгу "Война и мир" в формате .тхт, считаем все строки, а потом их разделим на слова и создадим одну большую строку из таких строк. Конечно, это сугубо умозрительная задача, нацеленная только на проверку скорости выполнения вычислений одним ядром и в много-поточном режиме, а также, что бы познакомиться с основными методами для таких вычислений. Осилившие статью до конца поймут, как мы можем это использовать в Ревит, и какое тут место у Revit API.

Заходим в Диспетчер макросов в Ревите и создаем макрос вот с таким именем "TestParallel". 

Добавим туда несколько библиотек:

Показать код C# →

using System.IO; //Библиотека для работы со StreamReader
using System.Threading.Tasks; // Библиотека для работы с параллельными задачами
using System.Collections.Concurrent; // Библиотека для работы со специальными потоко-безопасными коллекциями
using System.Timers;// Библиотека, содержащая доступ к системному времени на компьютере

И создадим наш рабочий класс, который будет содержать все необходимые члены для работы:

Показать код C# →

public class TestParallelProcessing 
        {
            private String fileTXT = @"E:\War_and_Peace.txt"// Путь, содержащий расположение файла книги "Война и мир" в фомате *.txt
            
            List <String> AllLines = new List<String>();    // Коллекция, которая будет содержащая все строки файла War_and_Peace.txt
            
            System.Collections.Concurrent.ConcurrentBag <String> objectText = new System.Collections.Concurrent.ConcurrentBag<String>(); // Потоко-безопасная коллекция для хранения результатов работы в разных потоках
            
            private String stParrallelResult = String.Empty; // В эту строку мы будем записывать слова
            
            private String stMainResult = String.Empty; // В эту строку мы будем записывать слова
        }

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

Показать код C# →

public void ReadTxtFile()
            {    
                using (System.IO.StreamReader sr = System.IO.File.OpenText(fileTXT)) // Начало, открываем файл .txt
                {
                    for (int x = 0; !sr.EndOfStream; x++)
                    {
                        AllLines.Add(sr.ReadLine()); // Записываем в коллекцию линию за линией
                    }
                } // Окончание, закрываем файл, освобождаем ресурсы     
            }

Теперь мы готовы сделать вишенку на торт. Создадим метод, разделяющий строки на слова, причем выполняться такая задача будет в разных потоках. Потом прямо в разных потоках запишем данные в нашу потоко-безопасную коллекцию. А когда работа будет выполнена, запишем слова из нашей потоко-безопасной коллекции в мега длинную строку, опять же, в нескольких потоках. Конечно же, замерим время на выполнение такой операции. Просто вернем в этом методе время, затраченное на выполнение всех действий. Да, в самом методе писанины будет меньше )

Показать код C# →

 public String WorkingParallel()

            {
                List <string> temp = new List<string>();
               
                DateTime end;
                
                DateTime start = DateTime.Now; // Записываем текущее время
                 
                // Запускаем на параллельное выполнение каждой линии
    
                System.Threading.Tasks.Parallel.For(0, AllLines.Count, x => // Аналог цикла for для параллельного выполнения задачи, читаем подробнее на MDSN
                 {
                     
                       string[] lin = AllLines[x].Split(' '); //Делим строки на части используя пробелы в качестве разделителя
                       
                       foreach (string s in lin) 
                           {
                               objectText.Add(s); // Записываем каждую разделенную часть строки в потоко-безопасную коллекцию
                           }                                           
                                                        
                 }); // Завершаем параллельные задачи
                
                 // Запускаем на параллельное выполнение каждый элемент коллекции objectText
                                  
                 temp.AddRange(objectText); // Для начала помещаем потоко-безопасную коллекцию в в простой список, для удобства работы

                System.Threading.Tasks.Parallel.For(0, temp.Count, x =>
                 {
                     
                     stParrallelResult += temp[x]; // Записываем в строку значение коллекции каждого элемента 
                                                        
                 });                
                
                end = DateTime.Now; // Записываем текущее время
                
                TimeSpan ts = (end - start);
                
                return "Всего слов: " + temp.Count.ToString() + ". Время обработки: " + ts.TotalMilliseconds.ToString(); // Узнаем время выполнения всех задач и возвращаем текстовое значение
                
            }

Ну и последнее. Узнаем за сколько времени аналогичная задача будет выполнена в одном (главном) потоке. Вот соответствующий метод:

Показать код C# →

public String WorkingMainThred()

            {
                List <string> temp = new List<string>();
                
                DateTime end;
                DateTime start = DateTime.Now; // Записываем текущее время
                 
                // Запускаем задачу
    
                for(int x = 0; AllLines.Count > x; x++)
                 {
                     
                       string[] lin = AllLines[x].Split(' '); //Делим строки на части используя пробелы в качестве разделителя
                       
                       foreach (string s in lin) 
                           {
                               temp.Add(s); // Записываем каждую разделенную часть строки в простую коллекцию
                           }                                           
                                                        
                 } // Завершаем задачи
                
                 // Запускаем на выполнение каждый элемент коллекции

                 for(int x = 0; temp.Count > x; x++)
                 {
                     
                     stMainResult += temp[x]; // Записываем в строку значение коллекции каждого элемента
                                                        
                 }                
                
                end = DateTime.Now; // Записываем текущее время
               
                TimeSpan ts = (end - start);
                
                return "Всего слов: "  + temp.Count.ToString() + ". Время обработки: " + ts.TotalMilliseconds.ToString(); // Узнаем время выполнения всех задач и возвращаем текстовое значение
                
            }

 Добавим в наш макрос рабочий метод и проверим результаты: 

Показать код C# →

public void  TestParallel()
        
        {
            TestParallelProcessing tpp = new TestParallelProcessing();
            
            tpp.ReadTxtFile();
                
            string p = "Параллельные вычесления - " + tpp.WorkingParallel() + "\n";
            string m = "Выполнение в главном потоке  - " + tpp.WorkingMainThred() + "\n";
                        
            TaskDialog.Show("Сообщение", p + m);            
        }

Итог: 2 227 миллисекунд против 23 191 миллисекунды в пользу параллельных вычислений. Разница более чем в 10 раз. В более сложных задачах и вычислениях эта разница будет еще больше.

Вопрос: Как мы можем использовать это в реальных задачах в работе с Revit API?

Ответ: Обрабатываете предварительно исходные данные. Извлекайте необходимые параметры для работы из элементов Ревит и размещаете их в своих коллекциях или классах. Далее создавайте методы для параллельных вычислений, которые будут работать с извлеченными данными.

P.S.: Файл "War_and_Peace.txt" и итоговый скрипт "TestParallel.cs" качайте внизу статьи.

Поддержите мои бесплатные приложения для Revit. Нажмайте "Нравится"!

Узнавайте о новых расширениях для Revit подписавшись на страницу BIM3D в Твиттере!

Извините за предствленные неудобства. Всплывающее окно не побеспокоит Вас в ближайшее время!

Связанные новости ►

Назад