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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 03.05.2011, 13:46   #1  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,898 / 5672 (195) ++++++++++
Регистрация: 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   #2  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,894 / 3172 (113) ++++++++++
Регистрация: 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   #3  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Я тут в очередной раз занимался оптимизацией разноски розничных продаж и наткнулся еще на пару возможных улучшений.
1. Метод writeTaxAmount_W, который тут ранее оптимизировали вызывает в цикле метод CustVendInvoiceTrans.initFromTaxWorkTrans_RU(). Там выполняется неиндексируемый запрос к злосчастной темповухе TmpTaxWorkTrans. Единственный селективный фильтр там это номер лота InventTransId, но индекса по нему нету. Получается примерно такая трассировка
Нажмите на изображение для увеличения
Название: trace.jpg
Просмотров: 34
Размер:	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
Просмотров: 35
Размер:	107.8 Кб
ID:	13299
За это сообщение автора поблагодарили: trud (10), Logger (5).
Старый 03.02.2022, 21:03   #4  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,894 / 3172 (113) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от Masel Посмотреть сообщение
Еще тормозит российская корреспонденция фин. проводок. Так не хочется глубоко погружаться в это. Никто не правил случаем? Я там кое-что правил, но вот этот кошмар остался. Без единового вызова субд.
Для какой версии ?
Я для 2009-й кое что правил - некие частные случаи с накладными расходами.
Там проблема из-за нелинейной зависимости от числа строк при разноске. (где то на форуме была тема)

Где возможно -
1. отключали корреспонденцию.
2. для больших документов (более 300 строк) вместо одной накладной делали кучку накладных по 40 строк. тем самым линеаризовали зависимость времени разноски от числа строк. - у нас были компании для управленческого учета где число накладных было неважно, главное итоговые показатели. Вот там такое было применимо.
Старый 03.02.2022, 21:22   #5  
Masel is offline
Masel
Участник
 
39 / 537 (18) +++++++
Регистрация: 19.09.2007
Цитата:
Сообщение от Logger Посмотреть сообщение
Для какой версии ?
2012 R2. Подозреваю что существенно не поменялось с 2009. В CIL то оно еще сносно работает, но все равно бы хотелось оптимизировать. Там явно неэффективные эти пробеги по map бесконечные.
Старый 03.02.2022, 22:12   #6  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,894 / 3172 (113) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от Masel Посмотреть сообщение
В CIL то оно еще сносно работает, но все равно бы хотелось оптимизировать. Там явно неэффективные эти пробеги по map бесконечные.
В CIL быстрее за счет двух вещей
1. JIT (ну и в целом CIL шустрее чем p-code)
2. Асинхронный сборщик мусора.
Старый 03.02.2022, 22:42   #7  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,894 / 3172 (113) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от Masel Посмотреть сообщение
Там явно неэффективные эти пробеги по map бесконечные.
Да!
Да!

Это прямо бросается в глаза. Перебор всего мапа со сравнением со значением на каждом шаге. Очень неэффективный поиск значения сделан. Видимо автор закладывался на то что раз операция в памяти то все будет быстро. Как бы не так! Там слишком много элементов в мапе на большом числе строк документа получается.
Поскольку там не голый мап а класс обертка, то у меня была идея сделать в нем некий вспомогательный второй мап, который будет индексом к основному мапу и позволит не перебирать все элементы первого мапа для поиска значений. Но руки не дошли.
Старый 04.02.2022, 08:51   #8  
SRF is offline
SRF
Участник
MCBMSS
Axapta Retail User
 
365 / 543 (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 / 76 (3) ++++
Регистрация: 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).
Теги
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, время: 20:17.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.