Вызовы функций из ex4-библиотек имеют существенный overhead?

 
Вопрос к разработчикам MQL4. Насколько медленнее вызовы функций из библиотек, реализованных на MQL4 и использующихся в виде ex4-файлов, по сравнению с вызовом функции реализованной в вызывающем модуле?

Просто сегодня столкнулся с забавной ситуацией. Имеется простейшая функция, реализация которой вынесена в отдельную библиотеку. Вызов этой функции из эксперта занимает по времени около 13 мс (расчитано как общее время потраченное на 2000 вызовов функции деленное на число вызовов). Это удивительно много!

Если эксперт анализирует последние 1000 баров, вызывая по одному разу эту функцию для каждого бара, то анализ этих 1000 баров занимает 13 секунд!

При этом, если я беру эту функцию и вставляю ее прямо в код эксперта, то среднее время, уходящее на вызов этой функции падает ниже пределов погрешности измерений.

Неужели вызовы библиотечных функций имеют такой overhead? Или я наткнулся на некоторый глюк?
 
bstone писал (а):
Очень интересный вопрос, часто используемые функции удобнее помещать в библиотеку, однако, если это настолько тормозит, как пишет bstone, придётся всё перетаскивать в основной код. Хотя Metaquotes предупреждали о более медленном вызове функций из вне эксперта, но не думал, что настолько.
 
Первый вызов функции из внешней библиотеки требует загрузки и инициализации этой библиотеки практически как вызов из DLL. Это занимает существенное время. Последующие вызовы уже будут работать быстро. Попробуйте провести замеры, отбросив первый вызов.

В любом случае, вызов из библиотек будет медленнее MQL4 кода, подключенного через #include.
 
MetaQuotes:
Первый вызов функции из внешней библиотеки требует загрузки и инициализации этой библиотеки практически как вызов из DLL. Это занимает существенное время. Последующие вызовы уже будут работать быстро. Попробуйте провести замеры, отбросив первый вызов.

Измерения проводились после инициализации библиотеки, т.к. перед ними в функции init() эксперта вызывается функция, инициализирующая внутреннее состояние библиотечного модуля, и следовательно библиотека загружается до измерений. Об этом также косвенно говорит тот факт, что среднее время вызова библиотечной функции не изменяется при изменении числа вызовов в измерительной серии.

В любом случае, вызов из библиотек будет медленнее MQL4 кода, подключенного через #include.


А вы можете дать какую-нибудь оценку хотя бы порядка этого отличия? Просто 13 мс и 0 мс это серьезная разница.

Просто хочется окончательно решить, использовать ex4-библиотеки или нет. Уж слишком серьезный удар они могут оказать по скорости оптимизации в случае, если такая разница обусловлена их технической реализацией.
 
bstone писал (а):
А вы можете дать какую-нибудь оценку хотя бы порядка этого отличия? Просто 13 мс и 0 мс это серьезная разница.

13 мс - это все в пределах погрешности системного таймера, нельзя на этом основании делать выводы. Перейдите за пределы 1000 мс и тогда выводы будут верные.

В любом случае, если Вы задумываетесь о скорости, то обязательно используйте включение полного исходного кода через #include "xxxxxx. mq4".
 
MetaQuotes:

13 мс - это все в пределах погрешности системного таймера, нельзя на этом основании делать выводы. Перейдите за пределы 1000 мс и тогда выводы будут верные.

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

В любом случае, если Вы задумываетесь о скорости, то обязательно используйте включение полного исходного кода через #include "xxxxxx. mq4".

Спасибо за совет, будем иметь в виду.
 
Я провел исследование, написав простой тест из двух файлов.

Тестовая функция в виде отдельной библиотеки (должна лежать в каталоге /libraries):
//+------------------------------------------------------------------+
//|                                                 externalfunc.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
#property library
//+------------------------------------------------------------------+
//| Тестовая функция                                                 |
//+------------------------------------------------------------------+
int ExternalFunc(int val1,int val2) { return(val1+val2); }
//+------------------------------------------------------------------+
Тестовый скрипт, совмещающий в себе тестирование обоих случаев:
//+------------------------------------------------------------------+
//|                                                testfunctions.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net// |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net//"
 
//---- импортируем внешнюю функцию
#import "externalfunc.ex4"
int ExternalFunc(int val1,int val2);
#import
//+------------------------------------------------------------------+
//| Тестовая функция                                                 |
//+------------------------------------------------------------------+
int InternalFunc(int val1,int val2) { return(val1+val2); }
//+------------------------------------------------------------------+
//| Замеряем время собственный функции                               |
//+------------------------------------------------------------------+
void TestInternalFunction()
  {
//---- замерим время прокрутки в цикле
   int start=GetTickCount();                    // засекаем время     
   for(int i=0;i<10000000;i++) InternalFunc(i,i); // долгий цикл
   start=GetTickCount()-start;                  // определяем прошедшее время
//---- распечатает результат
   Print("Internal function time: ",start," ms");
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Замеряем время внешней функции                                   |
//+------------------------------------------------------------------+
void TestExternalFunction()
  {
//---- замерим время выполнения первого вызова
   int start=GetTickCount();                    // засекаем время     
   ExternalFunc(1,1);                           // однократный вызов
   start=GetTickCount()-start;                  // определяем прошедшее время
//---- распечатает результат
   Print("External function time: ",start," ms (init)");
//---- замерим время прокрутки в цикле
   start=GetTickCount();                        // засекаем время     
   for(int i=0;i<10000000;i++) ExternalFunc(i,i); // долгий цикл
   start=GetTickCount()-start;                  // определяем прошедшее время
//---- распечатает результат
   Print("External function time: ",start," ms");
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Замеры                                                           |
//+------------------------------------------------------------------+
int start()
  {
//----
   TestInternalFunction();
   TestExternalFunction();
//----
   return(0);
  }
//+------------------------------------------------------------------+

Вот результаты:
Internal function time: 1547 ms
External function time: 0 ms (init)
External function time: 2640 ms
Перейдя к бОльшим по времени расчетам (более 1000 ms вместо 0 ms и 13 ms), результаты стали достоверными и на них не влияет погрешность таймера. Какой можно сделать вывод?

Время вызова функции из библиотеки на 70% медленнее вызова внутри своего модуля.

Я специально отметил, что речь идет именно о потерях во времени вызова, а не о том, что любая функция из библиотеки на 70% медленнее родной. Сама функция в библиотеке отрабатывается с той же скоростью, как и в родном модуле. Потеря происходит только на межмодульном вызове.

Отсюда следует еще один вывод - не стоит помещать мелкие и часто вызываемые функции во внешние библиотеки.
 
Renat:
Перейдя к бОльшим по времени расчетам (более 1000 ms вместо 0 ms и 13 ms), результаты стали достоверными и на них не влияет погрешность таймера. Какой можно сделать вывод?


Ну я же писал, что 0 и 13 мс это среднее время, вычисленное по результатам замеров общей длительностью около 27000 мс.

Но в любом случае спасибо за подтверждение. Т.о. небольшие часто используемые функции действительно лучше выносить в заголовочные файлы. Единственное неудобство - это предупреждения компилятора о том, что такие функции не используются. Они загромождают отчет об ошибках и лишний раз отвлекают внимание, когда таких функций много.
 
bstone:
Единственное неудобство - это предупреждения компилятора о том, что такие функции не используются. Они загромождают отчет об ошибках и лишний раз отвлекают внимание, когда таких функций много.
Если хотите гарантированно отключить сообщения компилятора об неиспользуемых функциях, то просто добавьте:
#property library
Тем самым компилятор перестанет контролировать неиспользуемые функции. Все будет работать нормально.
 

Отлично. Спасибо за совет.

 

Кстати, использование "#property library" имеет один, но очень убийственный эффект для экспертов - пропадают все параметры, объявленные как "extern". Поэтому такой финт для подавления предупреждений компилятора использовать для экспертов в общем случае нельзя.

Причина обращения: