AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 08.01.2003, 13:27   #1  
Lihgt is offline
Lihgt
Участник
 
48 / 70 (3) ++++
Регистрация: 16.04.2002
Адрес: Москва
Оптимизация класса Tax
Постановка задачи:
На основании модификации, полученной из офиса Колумбуса, изменить механизм расчета налогов при проведении накладной для повышения производительности.

Модификация:
В классе Tax изменен метод adjustAmount
PHP код:
void adjustAmount(ExchRate   _exchRate,
                  
ExchRate   _exchrateSecondary)
{
    if (
_exchRate)
    {
        
ttsbegin;

        while 
select forupdate taxWorkTrans
        where taxWorkTrans
.headingRecId  == headingRecId         &&
              
taxWorkTrans.exemptTax     == NoYes::No
            
// Оптимизация расчета налогов (c) KURA -->
           
&& taxWorkTrans.SourceRecId   == this.sourceRecId()
            
// Оптимизация расчета налогов (c) KURA <--
        
{
 ... 
Что мне известно по этой теме:
Действительно, при расчете налогов по многострочным заказам (закупкам) класс Tax делает очень много пересчетов суммы налогов. Особенно хорошо это видно, если на данную операцию натравить профайлер - Аксапта "уйдет в себя" на продолжительное время. Данная модификация должна решить эту проблему, но есть вопросы.

Вопросы:
1. Какой выигрыш в производительности может дать такое изменение кода?
2. Как это скажется на потомках Tax-а и на расчете "withoutSource"?
1+2 Стоит ли игра свеч, а результат труда (с учетом анализа своих потомков Tax-а)?
3. Кто-дь использует у себя такую оптимизацию?
4. Если все хорошо, то почему ее нет в SP и HF?

P.S. Не знаю как эта тема согласуется с авторскими правами.
P.S.S. Сам еще на занимался плотным анализом механизмов класса Tax и потомков. Поэтому, будет интересна любая инфа по теме.
Старый 20.11.2009, 12:45   #2  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
А кто применял данный фикс на реальном проекте ?

Вопрос связан с тем, что сомнения одолели. Даже не верится, что такой маленький фикс решит такую большую проблему. Где-то должен быть подвох.
Старый 03.05.2011, 13:46   #3  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,907 / 5717 (196) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Ладно, поскольку дискуссия с Logger перешла в эту ветку, пожалуй продолжу здесь.

Во первых, в версии 2009 этот метод (adjustAmount) изрядно подоптимизировали. Во вторых - он ничего такого не делает Логика достаточно простая:
  1. Он считает сумму в исходной валюте по каждому коду налога по документу (в реальности там группировка сложнее, но в первом приближении это только код налога)
  2. Он пересчитывает каждую строку налога в taxTmpWorkTrans - тупым перемножением суммы в валюте на курс. Данная операция выполняется для каждой строки отдельно ( а каждая строка в первом приближении - равно строке закупки или заказа - если у вас только НДС).
  3. Сумма в исходной валюте по каждому коду документа пересчитывается по новому курсу в валюту учета. Затем новая сумма размазывается по каждой строке с данным кодом.
  4. Данный метод вызывается только один раз.
На самом деле, сложность вычисления равна, грубо говоря n*3, где n-число строк в документе. (Один раз пробежались по n строкам чтобы посчитать суммы по кодам налогов, потом пробежались чтобы пересчитать суммы налога, потом еще раз пробежались чтобы скорректировать).
Однако есть одна загводка: Как известно, временные таблицы хранятся в памяти только если их размер не превышает 128 КБайт. Если размер превысил эту величину, то AOS выгружает их во временный файл, запросами и доступом к которому занимается сам AOS. А внутренний процессор запросов в AOS не быстрый и не оптимизировали его с прошлого столетия, вероятно он часто в fullscan скатывается
Размер одной записи в tmpTaxWorkTrans в стандартной конфигурации, составляет 793 байта. Получается что где-то после 160 строк в заказе/закупке, данные из таблицы выгружаются на диск и начинают страшно тормозить в запросах.

Если в новой версии (2012) tmpTaxWorkTrans переделали в SQL-ную временную таблицу, это должно заметно ускорить процесс. Также можно надеятся, что в 64битной версии сервера (а мне кажется 32битной и не будет) наконец-то отменили ограничение в 128 Кбайт под временную таблицу в памяти и теперь таблица будет храниться в памяти пока таковая имеется.

Последний раз редактировалось fed; 03.05.2011 в 13:49.
За это сообщение автора поблагодарили: Logger (5), ziva (2).
Старый 29.11.2011, 21:25   #4  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от fed Посмотреть сообщение
...
Однако есть одна загводка: Как известно, временные таблицы хранятся в памяти только если их размер не превышает 128 КБайт. Если размер превысил эту величину, то AOS выгружает их во временный файл, запросами и доступом к которому занимается сам AOS. А внутренний процессор запросов в AOS не быстрый и не оптимизировали его с прошлого столетия, вероятно он часто в fullscan скатывается
Размер одной записи в tmpTaxWorkTrans в стандартной конфигурации, составляет 793 байта. Получается что где-то после 160 строк в заказе/закупке, данные из таблицы выгружаются на диск и начинают страшно тормозить в запросах.

Если в новой версии (2012) tmpTaxWorkTrans переделали в SQL-ную временную таблицу, это должно заметно ускорить процесс. Также можно надеятся, что в 64битной версии сервера (а мне кажется 32битной и не будет) наконец-то отменили ограничение в 128 Кбайт под временную таблицу в памяти и теперь таблица будет храниться в памяти пока таковая имеется.
Кстати такой вариант оптимизации времянок мы попробовали.
Идея в том, чтобы заставить времянки создаваться в памяти.
Например, можно завести виртуальный диск, реально живущий в памяти и прописать его в переменную окружения как темповую папку для пользователя, под которым работает сервер приложения. В таком случае файлы *.$$$, содержащие времянки будут реально жить в памяти и работать быстро. К сожалению, это не решило всех проблем. Если поиск во времянке делать по неиндексированному полю, то время улучшается примерно в 6-7 раз. Если по индексированному, то в 2 раза. (Т.е. индексы на больших времянках тоже существенно влияют на скорость) Также примерно в 2 раза ускоряется операция вставки. Мы ожидали бОльшего ускорения, поэтому на боевой базе не стали это применять.

Тест проводили на примерно 100-200 тысячах записей буферов UnitConvert. Это составило примерно 50-100 мегабайт в $$$ файле на диске.
За это сообщение автора поблагодарили: S.Kuskov (5).
Старый 03.02.2022, 20:32   #5  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Я тут в очередной раз занимался оптимизацией разноски розничных продаж и наткнулся еще на пару возможных улучшений.
1. Метод writeTaxAmount_W, который тут ранее оптимизировали вызывает в цикле метод CustVendInvoiceTrans.initFromTaxWorkTrans_RU(). Там выполняется неиндексируемый запрос к злосчастной темповухе TmpTaxWorkTrans. Единственный селективный фильтр там это номер лота InventTransId, но индекса по нему нету. Получается примерно такая трассировка
Нажмите на изображение для увеличения
Название: trace.jpg
Просмотров: 36
Размер:	109.0 Кб
ID:	13298
В общем нужно добавить индекс и плюс пришлось еще поменять немного код на поиске.
X++:
    if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoRU]))
    {
        //+ sergey.m 03.02.2022 FRE_20421639_001
        if (!_sourceRecId)
        {
            select taxWorkTrans index hint InventTransIdx
                where taxWorkTrans.SourceTableId == _sourceTableId       &&
                      taxWorkTrans.InventTransId == _inventTransId       &&
                      taxWorkTrans.TaxDirection  != TaxDirection::UseTax &&
                      taxWorkTrans.TaxOrigin     != TaxOrigin::TaxReversed;
        }
        else
        {
        //- sergey.m 03.02.2022 FRE_20421639_001
            select taxWorkTrans
                where taxWorkTrans.SourceTableId  == _sourceTableId             &&
                      ((_sourceRecId &&
                        taxWorkTrans.SourceRecId == _sourceRecId) ||
                       (! _sourceRecId &&
                        taxWorkTrans.InventTransId == _inventTransId))          &&
                      taxWorkTrans.TaxDirection       != TaxDirection::UseTax   &&
                      taxWorkTrans.TaxOrigin          != TaxOrigin::TaxReversed;
        }
Хинт на inMemory срабатывает. Без него у меня улучшений не было. Не знаю как тут аксапта выбирает какой индекс использовать, думаю хватает первый попавшийся по совпадению поля.

2. В классе Tax метод lineTaxAmount. В начале метода проверяется что в таблице есть записи немного экзотическим методом, считая их количество. Я поменял так, хотя можно было наверное вообще убрать запрос, меня пока и так устраивает.
X++:
    if (this.taxParameters().TaxSpecifyLine)
    {
    if (this.taxParameters().TaxSpecifyLine)
    {
        //+ sergey.m 03.02.2022 FRE_20421639_001
        //select count(RecId) from taxWorkTrans;
        select firstOnly RecId from taxWorkTrans;
        //- sergey.m 03.02.2022 FRE_20421639_001

        if (taxWorkTrans.RecId > 0 && !this.useSubLedgerJournalLines())
        {
            // Posting out of TmpTaxWorkTrans
Еще тормозит российская корреспонденция фин. проводок. Так не хочется глубоко погружаться в это. Никто не правил случаем? Я там кое-что правил, но вот этот кошмар остался. Без единового вызова субд.

Нажмите на изображение для увеличения
Название: trace1.jpg
Просмотров: 37
Размер:	107.8 Кб
ID:	13299
За это сообщение автора поблагодарили: trud (10), Logger (5).
Старый 03.02.2022, 21:03   #6  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от Masel Посмотреть сообщение
Еще тормозит российская корреспонденция фин. проводок. Так не хочется глубоко погружаться в это. Никто не правил случаем? Я там кое-что правил, но вот этот кошмар остался. Без единового вызова субд.
Для какой версии ?
Я для 2009-й кое что правил - некие частные случаи с накладными расходами.
Там проблема из-за нелинейной зависимости от числа строк при разноске. (где то на форуме была тема)

Где возможно -
1. отключали корреспонденцию.
2. для больших документов (более 300 строк) вместо одной накладной делали кучку накладных по 40 строк. тем самым линеаризовали зависимость времени разноски от числа строк. - у нас были компании для управленческого учета где число накладных было неважно, главное итоговые показатели. Вот там такое было применимо.
Старый 03.02.2022, 21:22   #7  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Цитата:
Сообщение от Logger Посмотреть сообщение
Для какой версии ?
2012 R2. Подозреваю что существенно не поменялось с 2009. В CIL то оно еще сносно работает, но все равно бы хотелось оптимизировать. Там явно неэффективные эти пробеги по map бесконечные.
Старый 04.02.2022, 08:51   #8  
SRF is offline
SRF
Участник
MCBMSS
Axapta Retail User
 
372 / 562 (19) +++++++
Регистрация: 08.08.2007
Записей в блоге: 1
Цитата:
Сообщение от Masel Посмотреть сообщение
Я тут в очередной раз занимался оптимизацией разноски розничных продаж и наткнулся еще на пару возможных улучшений.
1. Метод writeTaxAmount_W, который тут ранее оптимизировали вызывает в цикле метод CustVendInvoiceTrans.initFromTaxWorkTrans_RU(). Там выполняется неиндексируемый запрос к злосчастной темповухе TmpTaxWorkTrans. Единственный селективный фильтр там это номер лота InventTransId, но индекса по нему нету. Получается примерно такая трассировка
На уровне идеи (не факт конечно, что будет быстрее) - можно же вообще убрать запрос в этом месте к темповой табличке для RU функциональности(наверное и в общем случае можно, но будет чуть сложнее), я может конечно что то забываю или не учитываю, но идея такая :

- на одну строку накладной одна запись в TmpTaxWorkTrans для RU, связь по номеру лота (по логике можно хранить и несколько записей, но кажется в этом случае особого профита по сравнению с индексом не будет)

- получается можно до цикла по строкам получить map - ключ inventTransId, значение запись TmpTaxWorkTrans(в методе который собственно и формирует всю TmpTaxWorkTrans, т.е. дополнительных проходов не потребуется).

- в цикле из мапа получать запись TmpTaxWorkTrans и ее отдавать в метод уже (в самом методе и чуть ниже придется поменять код, так что обрабатывать запись, а не курсор, но это не кажется сложным).
__________________
Sergey Nefedov
Старый 04.02.2022, 11:28   #9  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Цитата:
Сообщение от SRF Посмотреть сообщение
На уровне идеи (не факт конечно, что будет быстрее) - можно же вообще убрать запрос в этом месте к темповой табличке для RU функциональности(наверное и в общем случае можно, но будет чуть сложнее), я может конечно что то забываю или не учитываю, но идея такая :

- на одну строку накладной одна запись в TmpTaxWorkTrans для RU, связь по номеру лота (по логике можно хранить и несколько записей, но кажется в этом случае особого профита по сравнению с индексом не будет)

- получается можно до цикла по строкам получить map - ключ inventTransId, значение запись TmpTaxWorkTrans(в методе который собственно и формирует всю TmpTaxWorkTrans, т.е. дополнительных проходов не потребуется).

- в цикле из мапа получать запись TmpTaxWorkTrans и ее отдавать в метод уже (в самом методе и чуть ниже придется поменять код, так что обрабатывать запись, а не курсор, но это не кажется сложным).
Не факт что там одна запись, индексов уникальных нет и выбирется курсор без firstonly, внутри они его фетчат, делают next TmpTaxWorkTrans. Поэтому придется делать map InventTransId -> Set(Types::record) и потом внутри логику менять, чтобы он заместо фетча бегал по Set. Это можно сделать, но в моем сценарии нет смысла, т.к. индексированный поиск отрабатывает меньше милисекунды.
Старый 27.05.2022, 11:05   #10  
DarkSpirit22 is offline
DarkSpirit22
Участник
Аватар для DarkSpirit22
 
13 / 94 (4) ++++
Регистрация: 07.11.2013
Адрес: СПб
Цитата:
Сообщение от Masel Посмотреть сообщение
Я тут в очередной раз занимался оптимизацией разноски розничных продаж и наткнулся еще на пару возможных улучшений.

<...>

2. В классе Tax метод lineTaxAmount. В начале метода проверяется что в таблице есть записи немного экзотическим методом, считая их количество. Я поменял так, хотя можно было наверное вообще убрать запрос, меня пока и так устраивает.
X++:
    if (this.taxParameters().TaxSpecifyLine)
    {
    if (this.taxParameters().TaxSpecifyLine)
    {
        //+ sergey.m 03.02.2022 FRE_20421639_001
        //select count(RecId) from taxWorkTrans;
        select firstOnly RecId from taxWorkTrans;
        //- sergey.m 03.02.2022 FRE_20421639_001

        if (taxWorkTrans.RecId > 0 && !this.useSubLedgerJournalLines())
        {
            // Posting out of TmpTaxWorkTrans
Нашел еще одно место, где наличие записей через select count делается:
\Classes\TaxPost\saveAndPost

X++:
public void saveAndPost(LedgerPostingController _ledgerPostingController, SelectableDataArea _companyToPost = curext())
{
    this.initLedgerPosting(_ledgerPostingController);

    //+ Abramov_ 27.05.2022 TSK0000280_08
    //select count(RecId) from taxWorkTrans;
    select firstonly RecId from taxWorkTrans;
    //- Abramov_ 27.05.2022 TSK0000280_08

    if (taxWorkTrans.RecId > 0 && !this.useSubLedgerJournalLines())
Еще маленькая оптимизация:
При создании строк накладной покупки в PurchInvoiceJournalCreate.createJournalLine() поле vendInvoiceTrans.LineAmount рассчитывается без учета экземпляра класса Tax, который был рассчитан на предыдущем шаге (в PurchInvoiceJournalCreate.initTotals()).

Т.е. в initTotals мы формируем кэш проводок TaxUncommitted, и, если не передать Tax, мы делаем лишние запросы к тому же TaxUncommitted.

X++:
    //+ Abramov_ 27.05.2022 TSK0000280_08
    //vendInvoiceTrans.LineAmount     = vendInvoiceInfoLine.lineAmountExclTax(vendInvoiceJour.InvoiceDate);
    vendInvoiceTrans.LineAmount     = vendInvoiceInfoLine.lineAmountExclTax(vendInvoiceJour.InvoiceDate, this.parmTax());    
    //- Abramov_ 27.05.2022 TSK0000280_08
Еще мы решили в таблицах TmpTaxWorkTrans и TaxUncommitted установить св-во SaveContents = NO полям, которые относятся к функциональности других стран (напр. _IN, _BR). Их оказалось довольно много, TmpTaxWorkTrans "похудел" на треть.

Еще мы провели эксперимент с переводом TmpTaxWorkTrans в TempDB (и обновлением существующей функциональности соответственно). Пришли к выводу, что без существенного рефакторинга кода от версии TmpTaxWorkTrans в TempDB толку нет, т.к. производительность расчета налогов упала ровно в два раза.

Последний раз редактировалось DarkSpirit22; 27.05.2022 в 11:29.
За это сообщение автора поблагодарили: Logger (5).
Старый 03.05.2011, 14:30   #11  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Хм. Про вытеснение времянок с памяти на диск я и не подумал.
Хотя все равно как-то косячно. Думаю что можно переделать алгоритм так чтобы зависимость от числа строк была линейной.

Если получится - отпишу.
Старый 03.05.2011, 14:34   #12  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,907 / 5717 (196) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Цитата:
Сообщение от Logger Посмотреть сообщение
Хм. Про вытеснение времянок с памяти на диск я и не подумал.
Хотя все равно как-то косячно. Думаю что можно переделать алгоритм так чтобы зависимость от числа строк была линейной.

Если получится - отпишу.
Я бы побробовал для начала выгрузить tmptaxWorkTrans в настоящую таблицу с неким guidом в качестве дополнительного поля. Потом прогнал бы по ней все операции, а потом загрузил бы назад в tmpTaxWorkTrans. Изврат конечно - но может и поможет. Особенно если вставлять через recordInsertList.
Старый 03.05.2011, 14:40   #13  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Ого !
Радикально !

А я думал в мап запихнуть - чтобы гарантировать наличие данные в памяти. Или в RecordSortedList (правда хрен его знает - сбрасывает он данные на диск или нет. Если данных не много - поиск по нему идет примерно с той же скоростью что и по мапам)
Старый 03.05.2011, 15:07   #14  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,907 / 5717 (196) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Цитата:
Сообщение от Logger Посмотреть сообщение
Ого !
Радикально !

А я думал в мап запихнуть - чтобы гарантировать наличие данные в памяти. Или в RecordSortedList (правда хрен его знает - сбрасывает он данные на диск или нет. Если данных не много - поиск по нему идет примерно с той же скоростью что и по мапам)
Ну про RecordSortedList я тоже думал, но так проще
Вообще может быть еще можно построить кучу индексов по временной таблицe, оптмизированых под именно этот запрос
Старый 29.11.2011, 11:26   #15  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Поковырял еще тему с оптимизацией
см. еще
axdaily: SQL temporary tables in AX 2012

Оказалось, что нелинейность прячется не в классе Tax а при прописывании налогов в строки документа.

Например для класса PurchFormLetter_Invoice в методе writeTaxAmount_W
есть код
X++:
invoiceTrans.initFromTaxWorkTrans_RU(this.tmpTaxWorkTrans(vendInvoiceJour.RecId),
                                              tablenum(PurchLine),
                                              0,
                                              invoiceTrans.InventTransId);
А метод tmpTaxWorkTrans зачем то дублирует временную табличку.
Получается что мы перебираем N строк накладной и на каждой строке копируем времянку через while select { buffer.insert() } т.е. тоже выполняем N шагов. Итого сложность алгоритма N^2 - квадратичная.

Исправить можно например так :

X++:
protected void writeTaxAmount_W()
{
    VendInvoiceTrans invoiceTrans;
    CRSEFeatures_W   features = CompanyInfo::features_W();
    TmpTaxWorkTrans     GRD_locTmpTaxWorkTrans; //+GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011

    if (features != CRSEFeatures_W::PL &&
        features != CRSEFeatures_W::RU)
        return;

    if (! TaxParameters::find().TaxSpecifyLine)
        return;

    // GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 -->
     GRD_locTmpTaxWorkTrans = this.tmpTaxWorkTrans(vendInvoiceJour.RecId);
    // GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 <--

    while select forupdate invoiceTrans
        index hint InvoiceIdx
        where invoiceTrans.PurchID             == vendInvoiceJour.PurchId           &&
              invoiceTrans.InvoiceId           == vendInvoiceJour.InvoiceId         &&
              invoiceTrans.InvoiceDate         == vendInvoiceJour.InvoiceDate       &&
              invoiceTrans.InternalInvoiceId   == vendInvoiceJour.InternalInvoiceId &&
              invoiceTrans.NumberSequenceGroup == vendInvoiceJour.NumberSequenceGroup
    {        
        invoiceTrans.initFromTaxWorkTrans_RU(
// GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 -->
                                             //this.tmpTaxWorkTrans(vendInvoiceJour.RecId),
                                             GRD_locTmpTaxWorkTrans,
// GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 <--
                                             tablenum(PurchLine),
                                             0,
                                             invoiceTrans.InventTransId);
        invoiceTrans.doUpdate();
    }
}
Для других классов семейства FormLetter исправляется аналогично.
На документах из 1000 строк экономия времени составила примерно 10 минут. Для документов из 100 строк не замерял, но думаю что немного.

Интересно что эта ошибка тянется еще с Ax 3.0
За это сообщение автора поблагодарили: lev (5), gl00mie (10), someOne (6).
Старый 29.11.2011, 13:04   #16  
someOne is offline
someOne
Участник
Аватар для someOne
 
173 / 429 (15) +++++++
Регистрация: 11.12.2008
Адрес: Москва
Цитата:
Сообщение от Logger Посмотреть сообщение
Поковырял еще тему с оптимизацией
...
Например для класса PurchFormLetter_Invoice в методе writeTaxAmount_W
Да, существенная оптимизация, спасибо за приведенный способ !!!
Цитата:
Сообщение от Logger Посмотреть сообщение
Для других классов семейства FormLetter исправляется аналогично.
SalesFormLetter_Invoice все то же самое. Ужас какой то...

Надеюсь "Локализаторы" обратят внимание и исправят в новых версиях!

Для тех кто работает с большим количеством строк в заказах или закупках эта оптимизация будет очень полезна.
Старый 29.11.2011, 11:45   #17  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Кстати, иногда можно слышать мнение, что кешированием в памяти чего-либо можно решить большинство проблем производительности.
К сожалению, это не всегда так. Серебряной пули нет. Кривой алгоритм может поставить на колени даже память с мощным процессором.

Второй пример нелинейности при обработке накладной по закупке :
Стек вызовов :
...
PurchFormLetter.run
PurchFormLetter.createJournal
PurchFormLetter.insertJournal
PurchFormLetter_Invoice.updateNow
PurchFormLetter_Invoice.updateInventory
MarkupAdjustment::adjustInvoice
ledgerVoucherObject.postCurrencyDiff

в методе ledgerVoucherObject.postCurrencyDiff есть вызовы методов
ledgerVoucherObject.listCurrencyAmountCur
ledgerBondClient.log2Table
ledgerBondClient.currentLog

Время выполнения каждого метода также квадратично зависит от числа строк в накладной. Но тут все немного хуже чем в первом случае, потому что для накладной из 1000 строк у меня получились дублирования мапов с одновременной фильтрацией путем перебора 8-9 тысяч элементов. Этого уже не выдерживает даже супербыстрый проц и память. Самое интересно что в мооем примере не было никаких накладных расходов и судя по всему выполнение метода MarkupAdjustment::adjustInvoice можно было вообще исключить.

Пока добавил такую оптимизацию :
X++:
/// <summary>
///    Calculates MST difference per transaction and generates an equalizing transaction.
/// </summary>
/// <param name="_ledgerVoucher">
///    The ledger voucher for the transaction.
/// </param>
/// <param name="_ledgerPostingType">
///    The posting type for the transaction.
/// </param>
/// <param name="_ledgerAccount">
///    The ledger account for the transaction.
/// </param>
/// <param name="_dimension">
///    A dimension.
/// </param>
/// <param name="_sourceTableId">
///    The table ID of the posting source.
/// </param>
/// <param name="_sourceRecId">
///    The record ID of the posting source
/// </param>
/// <param name="_transactionTxt">
///    A transaction text.
/// </param>
/// <param name="_level">
///    The level; option.
/// </param>
public void postCurrencyDiff(
    LedgerVoucher       _ledgerVoucher,
    LedgerPostingType  _ledgerPostingType,
    LedgerAccount      _ledgerAccount,
    Dimension          _dimension,
    tableId            _sourceTableId,
    recId              _sourceRecId,
    TransactionTxt     _transactionTxt,
    Integer            _level = 0)
{
    LedgerVoucherTransObject    ledgerVoucherTransObject;
    RecordSortedList            rsL;
    TmpLedgerTrans              tmpLedgerTrans;
    boolean                     more;
    // <GEEU>
    TmpLedgerBondLogTable_RU    logTable;
    LedgerBondTransObject_RU    bondTransObject;
    // </GEEU>
    boolean                     GRD_LoadedBondInfo = false;//+GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011


    rsL = this.listCurrencyAmountCur(_level);

    // <GEEU>

    // GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 -->
    if (GRD5::isR4719_2())
    {
        // вынесли этот код (блок else) внутрь цикла ниже, так как вызовы ledgerBondClient.currentLog() и ledgerBondClient.log2Table(
        // могут быть сложны для расчета при большом числе строк в документе (нелинейные зависимости от числа строк содержатся )
        // а их результат иногда бывает и не нужен, поэтому раньше времени их не вызваем, а только по мере необходимости, т.е. на первом шаге цикла
    }
    else
    {
        if (ledgerBondClient)
        {
            logTable = ledgerBondClient.log2Table(ledgerBondClient.currentLog());
        }
    }
    // GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 <--

    // </GEEU>
    for (more = rsL.first(tmpLedgerTrans);
         more;
         more = rsL.next(tmpLedgerTrans))
    {
        ledgerVoucherTransObject = LedgerVoucherTransObject::newCreateTrans(
                                           this,
                                           _ledgerPostingType,
                                           _ledgerAccount,
                                           _dimension,
                                           tmpLedgerTrans.CurrencyCode,
                                           -tmpLedgerTrans.AmountCur,
                                           _sourceTableId,
                                           _sourceRecId);

        ledgerVoucherTransObject.parmTransTxt(_transactionTxt.txt());
        _ledgerVoucher.addTrans(ledgerVoucherTransObject);
        // <GEEU>

        if (ledgerBondClient && tmpLedgerTrans.AmountCur)
        {

            // GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 -->
            if (GRD5::isR4719_2())
            {
                if (!GRD_LoadedBondInfo)
                {
                    GRD_LoadedBondInfo = true;
                    if (ledgerBondClient)
                    {
                        logTable = ledgerBondClient.log2Table(ledgerBondClient.currentLog());
                    }
                }
            }
            // GRD_R4719_SpeedUpFormLetter_pkoz, pkoz, 24.11.2011 <--

            bondTransObject = ledgerBondClient.bondTransObject(ledgerBondClient.lastVrefId());
            while select logTable
                where logTable.CurrencyCode == tmpLedgerTrans.CurrencyCode   &&
                      logTable.Crediting    != bondTransObject.remainCrediting()
            {
                ledgerBondClient.bondVRef2VRef(bondTransObject.vrefId(), logTable.vRef);
                if (bondTransObject.remainAmountCur() == 0)
                {
                    break;
                    // </GEEU>
                }
            }
        // <GEEU>
        }
    }
}
// </GEEU>
т.е. если для конкретных строк нет проводок, то часть копирований не происходит, за счет чего тоже экономится прилично времени.

P.S.
Вообще, грустно, что обработка накладных совсем не оптимизировалась на обработку больших объемов информации.
За это сообщение автора поблагодарили: Wamr (10).
Старый 29.11.2011, 11:46   #18  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,936 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
GRD5::isR4719_2() - это признак "включенности" изменений. Должен возвращать true
Старый 29.11.2011, 15:12   #19  
Wamr is offline
Wamr
----------------
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
 
1,737 / 858 (32) +++++++
Регистрация: 15.01.2002
Адрес: Москва
Записей в блоге: 7
немного истории
За это сообщение автора поблагодарили: Logger (3), lev (5), someOne (6).
Старый 29.11.2011, 15:47   #20  
someOne is offline
someOne
Участник
Аватар для someOne
 
173 / 429 (15) +++++++
Регистрация: 11.12.2008
Адрес: Москва
Цитата:
Сообщение от Wamr Посмотреть сообщение
Мммда... С 2005 года - могли бы десять раз поправить уже. Мне казалось что люди из команды разработчиков Аксапта читают этот форум... Жаль. Может теперь заметят ?
Теги
faq, tax, налоги, оптимизация, производительность

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Вызов метода базового класса Eldar9x DAX: Программирование 15 22.03.2008 19:10
jerry-dynamics: tax codes Blog bot DAX Blogs 0 16.06.2007 11:20
Вызов класса из другого класса Protey DAX: Программирование 9 26.02.2007 11:01
передача курсора в два класса kitty DAX: Программирование 3 09.08.2006 13:21
Запустить метод класса loka DAX: Программирование 2 13.03.2006 15:40

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 14:15.