Многопоточность и распараллеливание

Автор Sprinter500, 28.10.19, 10:26:42

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

Sprinter500

Здравствуйте! Есть ли возможность применять многопоточность и распараллеливание в работе с API Компаса? Есть ли есть такая возможность, то как?
Это я к чему: сделал программную замену текста в в объектах типа "текст" (IDrawingText), "выноски" (ILeader, IPositionLeader) - в ней дерево получения объектов от более старших, проверка на Null и перебор последовательный. Однако есть стандартная библиотека замены текста и она работает в разы шустрее. Подозреваю что дело в многопоточности или распараллеливании, может быть реализована асинхронность вызовов. Или может у меня принцип перебора объектов неправильный? Или дело в том, что я писал оба варианта на C# и Delphi, а надо было на C++? Хотя когда дело касается графической прорисовки - тоже самое - стандартные библиотеки прорисовывают практически мгновенно(кажется что все сразу параллельно отрисовывается), а собственные через API - медленно и видно как все идет последовательно. Еще замечание - почему то если зажать ролик мыши работа с API идет быстрее, но все равно со стандартными RTW-библиотеками не сравнится.

В общем как мне увеличить скорость работы замены текста до стандартной библиотеки? В чем хитрость?

Vi2

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

Как правило, ускорение достигается применением массивных (т.е. с массивами информации) операций, чтобы не было большого количества передач данных от клиента в Компас и обратно.

Если таких операций нет, то вряд ли можно ускорить. ИМХО, удобство внешнего клиента стоит больше, хоть и нужно подождать.

Sprinter500

Цитата: Vi2 от 29.10.19, 22:13:37Стандартные RTW-библиотеки грузятся внутрь Компаса (т.е. инициатор загрузки сам Компас) и для них не осуществляется маршаллинг на межпроцессное или межпотоковое(межапартментное) взаимодействие, как это, скорее всего, реализовано у тебя, если твой клиент дёргает Компас через АПИ или СОМ.

Как правило, ускорение достигается применением массивных (т.е. с массивами информации) операций, чтобы не было большого количества передач данных от клиента в Компас и обратно.

Если таких операций нет, то вряд ли можно ускорить. ИМХО, удобство внешнего клиента стоит больше, хоть и нужно подождать.



У меня работа из под оконного приложения EXE идет через API5 и API7, а не из под RTW. То есть из самодельной пользовательской библиотеки RTW будет все работать быстро? Или только RTW созданные самим АСКОНом будут работать быстро?

Как считаете например такой код как-то можно ускорить или уже все хорошо в нем?:

                            IDrawingTexts drawingTexts = DrawingContainer.DrawingTexts;
                            if (drawingTexts != null)
                            {
                                if (cbTexts.Checked) ReplacesCount = drawingTexts.Count;
                                for (int j = 0; j < ReplacesCount; j++)
                                {
                                    IDrawingObject drawingObject = drawingTexts.DrawingText[j];
                                    if (drawingObject != null)
                                    {
                                        if (drawingObject.DrawingObjectType == DrawingObjectTypeEnum.ksDrDrawText)
                                        {
                                            IDrawingText drawingText = (IDrawingText)drawingObject;
                                            if (drawingText != null)
                                            {
                                                IText text = (IText)drawingText;
                                                if (text != null)
                                                {
                                                    text.Str = text.Str.Replace("A", "B");
                                                    drawingObject.Update();
                                                    drawingText = null;
                                                }
                                            }
                                        }
                                    }
                                    drawingObject = null;
                                }
                                drawingTexts = null;
                            }

Vi2

#3
Я не слишком хорошо знаком с объектной моделью Компаса. Предыдущее сообщение я дал на основании общих сведений о взаимодействии СОМ серверов. Из этих же сведений выскажу такие соображения по поводу кода.

1) drawingTexts.DrawingText[j] уже возвращает интерфейс IDrawingText, который также является и интерфейсом IDrawingObject. Так что и на этом интерфейсе можно запрашивать свойство DrawingObjectType. Т.е.

IDrawingText drawingText = drawingTexts.DrawingText[j];
if (drawingText != null && drawingText.DrawingObjectType == DrawingObjectTypeEnum.ksDrDrawText)
{ ...

На этом можно выиграть несколько обращений к серверу.

2) интерфейс IText сам имеет свойство Replace( BSTR SrcText, BSTR NewText, BOOL Case, BOOL WordOnly, BOOL ReplaceAll).

На этом также экономится одно обращение.

PS
В принципе, если критично время, то можно написать маленький RTW, который, будучи загруженным в Компас, сам может стать сервером и реагировать на внешние команды от стороннего приложения. Для этого нужно зарегистрировать свой объект и получить к нему доступ.

Sprinter500

Цитата: Vi2 от 30.10.19, 19:06:52Я не слишком хорошо знаком с объектной моделью Компаса. Предыдущее сообщение я дал на основании общих сведений о взаимодействии СОМ серверов. Из этих же сведений выскажу такие соображения по поводу кода.

1) drawingTexts.DrawingText[j] уже возвращает интерфейс IDrawingText, который также является и интерфейсом IDrawingObject. Так что и на этом интерфейсе можно запрашивать свойство DrawingObjectType. Т.е.

IDrawingText drawingText = drawingTexts.DrawingText[j];
if (drawingText != null && drawingText.DrawingObjectType == DrawingObjectTypeEnum.ksDrDrawText)
{ ...

На этом можно выиграть несколько обращений к серверу.

2) интерфейс IText сам имеет свойство Replace( BSTR SrcText, BSTR NewText, BOOL Case, BOOL WordOnly, BOOL ReplaceAll).

На этом также экономится одно обращение.

PS
В принципе, если критично время, то можно написать маленький RTW, который, будучи загруженным в Компас, сам может стать сервером и реагировать на внешние команды от стороннего приложения. Для этого нужно зарегистрировать свой объект и получить к нему доступ.


1. Спасибо, попробую сделать подобный образом.
2. И так тоже попробую, если конечно не возникнет проблема, решенная ранее на другой ветке форума - если менять свойство str прям у него, то пропадают назначенные стили текста и становятся по умолчанию для документа, а если редактировать его у ITextItem то все в порядке. Пришлось делать так.
3. Да хорошая идея, сделать рабочую RTW, которая будет управляться из внешнего графического Exe-приложения. Только как как передавать в RTW параметры?

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

Vi2

>Только как как передавать в RTW параметры?

1. После загрузки RTW в Компас создаётся свой СОМ объект, который регистрируется через функции подобные С++ RegisterActiveObject и удаляется после завершения через RevokeActiveObject. После регистрации этот объект доступен через GetActiveObject, точно так же как и сам Компас. Соответственно, после всего этого можно вызывать все его методы и передавать данные.

2. Или другой сценарий. После загрузки RTW в Компас эта RTW вызывает/создаёт СОМ объект, в который передаёт свой интерфейс.

3. Есть такой механизм, который называется ObjrefMoniker (функции CreateObjrefMoniker и GetDisplayName). Получается строчка текста, по которой можно получить доступ к СОМ объекту (функция CoGetObject). Эту строчку можно уже передавать между задачами как обычный текст.

>Вы не в курсе можно ли как-то в таблицах взять текст сразу всех ячеек? а то пробег по всем ячейкам тоже долго идет очень.

Я не совсем в курсе Компаса, но в Ворде/Экселе такое есть: можно одну ячейку за раз изменять, а можно сразу все в выделенной области Range. Для этого и используются массивные функции, где передаются массивы (SafeArray) - одномерные или двумерные. Реализовано ли такое в Компасе - не знаю. Хотя в твоём интерфейсе IText есть методы TextLine(i) по каждой строчке текста и Str() для всего текста целиком.

Немного посмотрел интерфейсы. Есть ITableRange, имеющий такой же смысл, как и в Ворде:

[propget, helpstring("Получить тексты из ячеек ввиде массива VT_ARRAY | VT_BSTR")]
HRESULT Texts([out, retval] VARIANT* PVal);
[propput]
HRESULT Texts([in] VARIANT PVal);

Sprinter500

Спасибо большое! Вы прям для меня новый неизвестный затерянный мир открыли !!! Как много я не знаю оказывается.

Sprinter500

Извините за долгое отсутствие ответа о результатах - был занят другой работой.
Что ускоряет:
1. Использование родной Replace у интерфейса IText ускоряет перебор объектов в 1,6-2 раза.
2. Также помогло - временного отключение визуальной оболочки КОМПАСа - об этом я узнал в другой теме. Это ускоряет в 5-6 раз.

До опробования распараллеливания по ядрам процессора и полного переноса функционала в RTW-библиотеку я пока не дошел, не успел.

Осталась проблема с медленным перебором ячеек таблицы. Было предложено использовать ITableRange и получать массив строк в виде VT_ARRAY | VT_BSTR, но что-то я делаю не правильно похоже и это не работает. Тексты то считываются, а изменить их не получается. Вот пример нерабочего кода:


        void RENTable2(IDrawingTable drawingTable, string A, string B) // Переименование в таблицах
        {
            if (drawingTable != null)
            {
                ITable table = (ITable)drawingTable;
                if (table != null)
                {
                    ITableRange tableRange = (ITableRange)table.Range[0, 0, table.RowsCount - 1, table.ColumnsCount - 1];
                    if (tableRange != null)
                    {
                        //messagebox.show("есть");
                        Array textsArray = (Array)tableRange.Texts;
                        for (long w=0; w<textsArray.LongLength; w++)
                        {
                            string s = textsArray.GetValue(w).ToString();
                            s = s.Replace(A, B);
                            textsArray.SetValue(s,w);
                        }

                        //foreach (string txt in textsArray)
                        //{
                        //    txt = txt.Replace(A, B);
                        //    //string text = (string)obj;
                        //    //if (text != null)
                        //    //{
                        //    //    MessageBox.Show(text);
                        //    //}
                        //}
                    }
                    drawingTable.Update();
                }
                table = null;
            }
            drawingTable = null;
        }

Vi2

Array textsArray = (Array)tableRange.Texts; // Это получение данных
for (long w=0; w<textsArray.LongLength; w++)
{
    string s = textsArray.GetValue(w).ToString();
    s = s.Replace(A, B);
    textsArray.SetValue(s,w);
}
tableRange.Texts = textsArray; // Это обратная функция установки измененных данных. Синтаксис может быть неточен.

Sprinter500

Цитата: Vi2 от 21.11.19, 22:58:41tableRange.Texts = textsArray; // Это обратная функция установки измененных данных. Синтаксис может быть неточен.

Вот на этой обратной функции вылазит ошибка:
System.Runtime.InteropServices.COMException
  HResult=0x80010105
  Сообщение = Ошибка на сервере. (Исключение из HRESULT: 0x80010105 (RPC_E_SERVERFAULT))


Вероятно требуется преобразование типа объекта textsArray в тип соответствующий tableRange.Texts, только как?

Vi2

Цитата: Sprinter500 от 22.11.19, 07:03:42Вот на этой обратной функции вылазит ошибка:
System.Runtime.InteropServices.COMException
  HResult=0x80010105
  Сообщение = Ошибка на сервере. (Исключение из HRESULT: 0x80010105 (RPC_E_SERVERFAULT))
Вероятно требуется преобразование типа объекта textsArray в тип соответствующий tableRange.Texts, только как?
В документации написано, что нужно передавать массив строк. Как это выглядит на Шарпе, я не в курсе.

Sprinter500

Провел эксперимент создал в Delphi библиотеку RTW, которая меняет определенный текст в таблице, без использования ITableRnge, то есть простым перебором ячеек путем 2х вложенных циклов For перебирающих строки и столбцы. RTW обычная, не Automation. То есть не ActiveX DLL. Далее создал тестовую таблицу 44х44 ячейки. Библиотека отработала за доли секунды, тогда как созданное в C# (а там только ActiveX и по другому никак) - работало около 20 сек. По итогу получается обычный COM при работе с КОМПАС шустрее чем ActiveX в десятки раз.

Теперь вопрос как на рабочем месте без прав админа (они у сисадминов, дергать их каждый раз не хочется) и без участия Delphi писать на C# шустрые варианты? Можно конечно управляемые извне обычные RTW, но скоро Delphi удалят сисадмины, останется только C#. Или может быть на Lazarus тоже возможно RTW клепать? У C++ не нравится синтаксис.