Что быстрее? глобальные или локальные переменные? - страница 2

 

Спасибо С-4 за ответ. Особенно насчет большого времени на изменение размеров массивов.

По поводу глобальных / локальных переменных решил поставить эксперимент. Написал такую функцию

void OnTick()
  {
// 29 c - на глобальных переменных
   v1=1.212;
   v2=1001;
   for (int i=0;i<20000;i++){
      if(v1<v2){v1=v1+1.214;}else{v2=v2*1.1201;}
   }

  /*

//36 c - на локальных переменных
   double v3=1.212;
   double v4=1001;

   for (int i=0;i<20000;i++){
      if(v3<v3){v3=v3+1.214;}else{v4=v4*1.1201;}
   }
*/
  
  }

Тут 2 участка кода один для глобальных, второй для локальных переменных. Комментируя по очереди их делал замер времени работы тестера  на промежутке в 7 дней, чтобы было много запусков OnTick()

В результате на глобальных переменных 1 проход занял 29 секунд, на локальных 36 секунд.

Потом уменьшил цикл до 20 for (int i=0;i<20;i++) и запустил проход за последние 2 года, в результате получил одинаковый результат - по 23 секунды и для локальных и для глобальных переменных. Тут объем вычислений мал, 20 сравнений и 20 арифметических операций. Думаю в большинстве советников за 1 тик происходит не больше.

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

 

Локальная переменная создается временно при вызове функции и освобождает память при завершении, а это требует времени.

Глобальная создается один раз при инициализации эксперта или индикатора. 

 
Ну, была надежда, что они в регистрах процессора хранятся и что работа с ними быстрее будет. Оказалось, что нет(
 
sandex:

Локальная переменная создается временно при вызове функции и освобождает память при завершении, а это требует времени.

Глобальная создается один раз при инициализации эксперта или индикатора. 

Локальная переменная, как правило,  размещается на стеке. А значит, ее размещение происходит автоматически при вызове функции, никаких действий по размещению локальная переменная больше не требует.

Глобальная переменная - если имеется ввиду глобальная переменная как "доступная из любого места программы" - то она тоже может быть размещена на стеке программы. Но если имеется ввиду глобальная переменная, которая занимает особую область памяти в терминале - то, да, для ее размещения требуется время, но оно требуется один раз.

 
И для этого необходимо тратить время, автоматика тоже не мгновенна.
 
C-4: ... Но если делать все по-взрослому, то хеширующая функция в словаре по-любому понадобиться, ибо как иначе ты сможешь гарантировать уникальность объекта и его эффективную сортировку?
C-4: ... Словарь в .Net содержит пару "ключ - значение", где уникальность ключа гарантирует хеш функция. Хеш таблица содержит только уникальные объекты, уникальность которых гарантируется той же хеш функцией.
C-4: ... А дело здесь в том, что словарь как и хеш таблица использует для идентификации объекта функцию GetHeshCode(). В первом случае эта функция не переопределяется и наследуется от базового объекта Object всех классов как есть. Если хеши не равны, то объекты разные и могут существовать в одной уникальной коллекции. Но дело в том что сама хеш функция использует понятие равенства объектов. ...

Уникальность объекта не зависит от его хеш кода.
Для примера переопределите GetHeshCode() чтобы она всегда возвращала одну и ту же константу для объектов какого-нибудь класса. Подозреваю что вы потом сможете без труда нашпиговать Dictionary любыми объектами данного класса (как Key). А hashtable лежащая в основе словаря просто выродиться в список. GetHeshCode() переопределяют тогда, когда планируют использовать экземпляры объекта как ключи в хеш таблице (как Key). Ни больше ни меньше. И желательно что бы её логика зависела от тех же полей от которых зависит Equals(). Но это не обязательно.

Более того у вас в примере СPerson и SPerson вообще выступают в роли ключей (как Key). Для них ни в каком случае GetHeshCode() не будет вызван, только Equals() на некоторых этапах.

Я не умничаю и не хочу никого задеть, совсем недавно сам больно несколько раз наступал на похожие грабли.

P.S. Ещё есть скользкие кейсы с mutable/immutable ключами, кейсы с зацикливанием при получение хеш кода для комплексных объектов. Но это всё уже и так сильно далеко от "локальных и глобальных переменных"

Возможно в С# всё не так ))

P.S.S Вычеркнул чушь

 
Roffild:
"Преждевременная оптимизация — это корень всех бед" (c) Дональд Кнут

Сначала нормальный код, а потом профайлером по нему.

Тоже плюсую.

Поднятые вопросы настолько далеки от реалий программирования на mql, что смысла их обсуждать нет ни какого.

 

Разве что про массивы правильно заметили (перераспределение - медленное). Но на это так или иначе наткнешься, немного с ними поработав.
И для ускорения, кстати, совсем не обязательно знать финальный размер, достаточно увеличивать размер массива порциями (например, по 1000 элементов).
В пятерочной функции есть даже специальный параметр для этого:

int  ArrayResize(
   void&  array[],              // массив, переданный по ссылке
   int    new_size,             // новый размер массива
   int    reserve_size=0        // резервное значение размера (избыточное)
   );
 
komposter:

Тоже плюсую.

Поднятые вопросы настолько далеки от реалий программирования на mql, что смысла их обсуждать нет ни какого.

Разве что про массивы правильно заметили (перераспределение - медленное). Но на это так или иначе наткнешься, немного с ними поработав.
И для ускорения, кстати, совсем не обязательно знать финальный размер, достаточно увеличивать размер массива порциями (например, по 1000 элементов).
В пятерочной функции есть даже специальный параметр для этого:

Да, обычно в классах-массивах так и происходит. В Net например, увеличение массива происходит автоматически, количество элементов выбирается кратным двум, т.е. сначала на 2, потом на 4, 8, 16, 32 и т.д.

А вообще да, мы с обсуждением словарей ушли далеко в оффтоп. Хотя работу функции GetHeshCode() я проверю) 

 
elibrarius:

Спасибо С-4 за ответ. Особенно насчет большого времени на изменение размеров массивов.

По поводу глобальных / локальных переменных решил поставить эксперимент. Написал такую функцию

void OnTick()
  {
// 29 c - на глобальных переменных
   v1=1.212;
   v2=1001;
   for (int i=0;i<20000;i++){
      if(v1<v2){v1=v1+1.214;}else{v2=v2*1.1201;}
   }

  /*

//36 c - на локальных переменных
   double v3=1.212;
   double v4=1001;

   for (int i=0;i<20000;i++){
      if(v3<v3){v3=v3+1.214;}else{v4=v4*1.1201;}
   }
*/
  
  }

Тут 2 участка кода один для глобальных, второй для локальных переменных. Комментируя по очереди их делал замер времени работы тестера  на промежутке в 7 дней, чтобы было много запусков OnTick()

В результате на глобальных переменных 1 проход занял 29 секунд, на локальных 36 секунд.

Потом уменьшил цикл до 20 for (int i=0;i<20;i++) и запустил проход за последние 2 года, в результате получил одинаковый результат - по 23 секунды и для локальных и для глобальных переменных. Тут объем вычислений мал, 20 сравнений и 20 арифметических операций. Думаю в большинстве советников за 1 тик происходит не больше.

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

Не там Вы производительность ищете. В реальных проектах, пока код дойдет до прямого перебора он пройдет еще сотню узких мест. Вот например, в советнике, код которого Вы привели может содержаться еще сотня а то и тысяча строк. Вы уверены, что перебор в цикле самое узкое место в Вашем проекте? Самый лучший способ добиться высокой производительности это написать приложение с простой ясной архитектурой, без избыточного кода. А сделать это гораздо сложней чем оптимизировать цикл for.
 
C-4: ... Хотя работу функции GetHeshCode() я проверю) 

Облегчаю работу

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Ассциируем с каждой личностью ее рейтинг на MQL5:
            Dictionary<CPerson, int> DictionaryRaiting = new Dictionary<CPerson, int>();

            //А теперь побезобразничаем и нашпигуем словарик разными ключами - личностями (каждый ключ это новый объект),
            //возвращающими ОДИН И ТОТ ЖЕ HashCode:
            for (int i = 0; i < 5; i++ )
            {
                DictionaryRaiting.Add(new CPerson("Вася"), 1910);
                Console.Out.WriteLine("Dictionary size: " + DictionaryRaiting.Count);
            }
            Console.ReadKey();

        }
    }

    /// <summary>
    /// Личность
    /// </summary>
    class CPerson
    {
        public CPerson(string name)
        {
            mName = name;
        }

        public string Name
        {
            get { return mName; }
        }
        /// <summary>
        /// Внутреннее имя человека.
        /// </summary>
        private string mName;

        public override int GetHashCode()
        {
            Console.Out.WriteLine("GetHashCode() was called and returns 1");
            return 1;
        }
    }

}


Посмотрим на консольку и сделаем вывод


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