Форум программистов, компьютерный форум, киберфорум
ООП и паттерны
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.64/11: Рейтинг темы: голосов - 11, средняя оценка - 4.64
23 / 22 / 4
Регистрация: 25.06.2018
Сообщений: 169

Оптимальность использования "new" или объявление переменных в циклах

25.02.2020, 19:40. Показов 2444. Ответов 13

Студворк — интернет-сервис помощи студентам
Часто вижу использование "new" или объявление переменных в больших циклах. К примеру, при обновлении изображения.

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

А. Инициализация В цикле
C#
1
2
3
4
5
class A {
    while(true) {
        new x = .....;
    }
}
Вот я и думаю, а не будет ли все это работать быстрее если выделять память до цикла и не трогать ее?
Интересует конкретно C# или Java.

А. Инициализация ДО цикла
C#
1
2
3
4
5
6
class B {
    X x = new X();
    while(true) {
        x = .....;
    }
}
(не придирайтесь к примеру, это для наглядности)
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
25.02.2020, 19:40
Ответы с готовыми решениями:

Обьявление переменных в циклах и до
Возник вопрос почему? Без обьявления int x = 1; до цикла возникает ошибка при компиляции программы ... Хотя в книжке обьявление типа и...

Найти в файле с текстом программы объявление переменных и подсчитать количество переменных
Есть задача: найти в файле с текстом программы объявление переменных и подсчитать количество переменных. Т.е. ищем строку с int,...

Объявление структуры с возможностью использования в нескольких формах
Народ, создаю программу(MDI форму). В одной из дочерних форм есть структура typedef struct{ float coord; } coordS; Оказалось,...

13
Модератор
Эксперт функциональных языков программирования
3134 / 2281 / 469
Регистрация: 26.03.2015
Сообщений: 8,877
25.02.2020, 23:09
Цитата Сообщение от Серый74 Посмотреть сообщение
Вот я и думаю, а не будет ли все это работать быстрее если выделять память до цикла и не трогать ее?
Лет 10-15 тому назад имело бы смысл. Сейчас уже нет. Сборщик мусора в c# заточен как раз под такой сценарий использования.
1
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18264 / 14189 / 5366
Регистрация: 17.03.2014
Сообщений: 28,876
Записей в блоге: 1
27.02.2020, 11:19
Серый74, если есть возможность вынести код создания объекта за пределы цикла, то лучше это сделать. Потому что, несмотря на то что сборщик мусора оптимизирован на быструю очистку нулевого поколения (в .NET по крайней мере), не надо создавать ему лишнюю работу.

Цитата Сообщение от Серый74 Посмотреть сообщение
Понятно что в ООП-шных компиляторах мусор чистится на корню.
1) Между ООП и сборкой мусора нет связи
2) Компилятор не имеет отношения к сборке мусора
3) "Чистка на корню" и сборка мусора это не одно и тоже. Если говорить про .NET и Java, то сборщик мусора запускается далеко не сразу.

Цитата Сообщение от Серый74 Посмотреть сообщение
о есть, как я понимаю, на каждой итерации цикла "new" занимает память, а по завершении чистильщик ее освобождает.
Это не так. Запуск сборщика мусора не привязан к времени жизни локальных переменных. Объект созданный в цикле продолжает висеть в памяти пока сборщик не удалит его. Когда именно это произойдет мы не можем предсказать.

Цитата Сообщение от Серый74 Посмотреть сообщение
Часто вижу использование "new" или объявление переменных в больших циклах.
Чем объявление переменных не угодило то?

Цитата Сообщение от Серый74 Посмотреть сообщение
К примеру, при обновлении изображения.
Не помешает привести конкретный пример.

Цитата Сообщение от Серый74 Посмотреть сообщение
Вот я и думаю, а не будет ли все это работать быстрее если выделять память до цикла и не трогать ее?
Может да, а может нет. Нужно проверять путем профилирования.

Цитата Сообщение от Серый74 Посмотреть сообщение
Инициализация ДО цикла
C#
1
2
3
4
5
6
class B {
    X x = new X();
    while(true) {
        x = .....;
    }
}
А в строке №4 инициализации нет что-ли?
0
23 / 22 / 4
Регистрация: 25.06.2018
Сообщений: 169
27.02.2020, 20:40  [ТС]
Цитата Сообщение от OwenGlendower Посмотреть сообщение
Не помешает привести конкретный пример.
Вот к примеру тут сразу штук 20 "new" в цикле анимации.
Анимация взлета ракеты
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Graphics g1 = e.Graphics;
            Graphics g2 = e.Graphics;
            Graphics g3 = e.Graphics;
            g.Clear(Color.Blue);
            SolidBrush peg = new SolidBrush(Color.Red);
            SolidBrush peg1 = new SolidBrush(Color.Yellow);
            SolidBrush peg2 = new SolidBrush(Color.Yellow);
            SolidBrush peg3 = new SolidBrush(Color.Aqua);
            GraphicsPath gp = new GraphicsPath(FillMode.Winding);
            GraphicsPath gp1 = new GraphicsPath(FillMode.Winding);
            GraphicsPath gp2 = new GraphicsPath(FillMode.Winding);
            GraphicsPath gp3 = new GraphicsPath(FillMode.Winding);
            gp.AddPolygon(new Point[] { new Point(450, y), new Point(465, y + 100), new Point(485, y + 100), new Point(500, y), new Point(475, y - 30), new Point(450, y) });
            gp1.AddPolygon(new Point[] { new Point(465, y + 100), new Point(445, y + 120), new Point(445, y + 90), new Point(460, y + 70), new Point(465, y + 100) });
            gp2.AddPolygon(new Point[] { new Point(485, y + 100), new Point(505, y + 120), new Point(505, y + 90), new Point(490, y + 70), new Point(485, y + 100) });
            gp3.AddEllipse(465, y + 5, 20, 20);
            g.FillPath(peg, gp);
            g1.FillPath(peg1, gp1);
            g2.FillPath(peg2, gp2);
            g3.FillPath(peg3, gp3);
        }
Или вот пример попроще:
Анимация
C#
1
2
3
4
5
6
7
        private void Form1_Paint(object sender, PaintEventArgs e)
        {     
            Graphics g = e.Graphics;
           g.Clear(Color.White);           
            Pen b = new Pen(Color.Gray);
            g.DrawRectangle(b,150, 250, 100, 100);
        }
При каждой перерисовке кадра отрабатываются "new" для SolidBrush, GraphicsPath, Point, Pen, а еще попадаются Color, Cursor, Rectangle и т.д. Вот мне и кажется, что каждый раз, допустим 60 раз в секунду, выделяется по несколько байт под одни и те же значения. И на это уходят лишние такты.
С чисткой мусора вроде все понял, она тут вообще не сработает.
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18264 / 14189 / 5366
Регистрация: 17.03.2014
Сообщений: 28,876
Записей в блоге: 1
28.02.2020, 00:57
Серый74, я не большой специалист по графике, но я бы переписал код с меньшим количеством new. Нужно только убедиться что это не ломает программу. Если не ломает, то можно также посмотреть с помощью Process Explorer сколько памяти и, что важнее всего, GDI ресурсов потребляет программа. Потому что тут активно создаются объекты завязанные на GDI и ее ресурсы могут сильно напрягаться при активной анимации.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    g.Clear(Color.Blue);
    SolidBrush peg = (SolidBrush)Brushes.Red;
    SolidBrush peg1 = (SolidBrush)Brushes.Yellow;
    SolidBrush peg2 = peg1;
    SolidBrush peg3 = (SolidBrush)Brushes.Aqua;
    GraphicsPath gp = new GraphicsPath(FillMode.Winding);
    GraphicsPath gp1 = new GraphicsPath(FillMode.Winding);
    GraphicsPath gp2 = new GraphicsPath(FillMode.Winding);
    GraphicsPath gp3 = new GraphicsPath(FillMode.Winding);
    gp.AddPolygon(new Point[] { new Point(450, y), new Point(465, y + 100), new Point(485, y + 100), new Point(500, y), new Point(475, y - 30), new Point(450, y) });
    gp1.AddPolygon(new Point[] { new Point(465, y + 100), new Point(445, y + 120), new Point(445, y + 90), new Point(460, y + 70), new Point(465, y + 100) });
    gp2.AddPolygon(new Point[] { new Point(485, y + 100), new Point(505, y + 120), new Point(505, y + 90), new Point(490, y + 70), new Point(485, y + 100) });
    gp3.AddEllipse(465, y + 5, 20, 20);
    g.FillPath(peg, gp);
    g.FillPath(peg1, gp1);
    g.FillPath(peg2, gp2);
    g.FillPath(peg3, gp3);
}
C#
1
2
3
4
5
6
7
private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    g.Clear(Color.White);
    Pen b = Pens.Gray;
    g.DrawRectangle(b, 150, 250, 100, 100);
}
Добавлено через 1 минуту
Более того - для скорости все эти brush-ы, pen-ы можно по идее закэшировать в полях класса.

Добавлено через 1 час 27 минут
Цитата Сообщение от Серый74 Посмотреть сообщение
Вот к примеру тут сразу штук 20 "new" в цикле анимации.
Те которые new Point можно не брать в расчет т.к. Point это значимый тип.
0
Модератор
Эксперт функциональных языков программирования
3134 / 2281 / 469
Регистрация: 26.03.2015
Сообщений: 8,877
28.02.2020, 08:51
Я бы вынес константные (по отношению к переменной цикла) вычисления за пределы цикла.
0
23 / 22 / 4
Регистрация: 25.06.2018
Сообщений: 169
28.02.2020, 21:50  [ТС]
Решил попробовать потестить разные варианты объявления переменной в консоли.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System;
using System.Diagnostics;
 
namespace Test_new
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            unsafe
            {
                //int variable; //Вариант 1 - переменная вне цикла
 
                stopwatch.Start();
                for (int i = 0; i < 1000; i++)
                {
                    int variable; //Вариант 2 - переменная в цикле
 
                    int* pointer = &variable; //указатель
                    ulong address = (ulong)pointer; //адрес
                    Console.Write("Адрес переменной: {0}", address);
                    Console.WriteLine(" и ее значение: {0}", variable++);
                }
                stopwatch.Stop();
                Console.WriteLine(stopwatch.ElapsedMilliseconds);
            }
        }
    }
}
Время выполнения - вроде одинаковые.
Адреса памяти - одинаково не меняются.
Но что самое прикольное, при переобъявлении переменной в цикле, ее значение даже не обнуляется.

Windows Batch file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
Адрес переменной: 430846763752 и ее значение: 987
Адрес переменной: 430846763752 и ее значение: 988
Адрес переменной: 430846763752 и ее значение: 989
Адрес переменной: 430846763752 и ее значение: 990
Адрес переменной: 430846763752 и ее значение: 991
Адрес переменной: 430846763752 и ее значение: 992
Адрес переменной: 430846763752 и ее значение: 993
Адрес переменной: 430846763752 и ее значение: 994
Адрес переменной: 430846763752 и ее значение: 995
Адрес переменной: 430846763752 и ее значение: 996
Адрес переменной: 430846763752 и ее значение: 997
Адрес переменной: 430846763752 и ее значение: 998
Адрес переменной: 430846763752 и ее значение: 999
535
Кто подскажет, как подвязать указатель к объекту, чтоб с использованием "new" попробовать?
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18264 / 14189 / 5366
Регистрация: 17.03.2014
Сообщений: 28,876
Записей в блоге: 1
29.02.2020, 00:02
Цитата Сообщение от Серый74 Посмотреть сообщение
Время выполнения - вроде одинаковые.
Адреса памяти - одинаково не меняются.
Логично. Потому что здесь нет никаких оснований для отличий.

Цитата Сообщение от Серый74 Посмотреть сообщение
Но что самое прикольное, при переобъявлении переменной в цикле, ее значение даже не обнуляется.
Компиляторы не стал генерировать код о котором его не просили и от которого нет пользы.

Цитата Сообщение от Серый74 Посмотреть сообщение
Кто подскажет, как подвязать указатель к объекту, чтоб с использованием "new" попробовать?
Зачем? Локальные переменные нет смысла тестировать. А если начать использовать ссылочные типы, то это будет тест на скорость работы CLR (выделение памяти и сборка мусора) который врядли даст что-то полезное.
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
29.02.2020, 10:53
Цитата Сообщение от Серый74 Посмотреть сообщение
Вот к примеру тут сразу штук 20 "new" в цикле анимации.
Это плохой код. И new здесь не самая большая проблема.
Классы SolidBrush и GraphicsPath - это IDisposable классы, и к ним привязаны ресурсы системы.
И для них обязательно нужно вызывать Dispose, иначе память очень быстро закончится.
0
23 / 22 / 4
Регистрация: 25.06.2018
Сообщений: 169
29.02.2020, 11:21  [ТС]
Цитата Сообщение от OwenGlendower Посмотреть сообщение
Компиляторы не стал генерировать код о котором его не просили и от которого нет пользы.
А я думал что как раз просил, когда писал "int variable;", но похоже что в циклы вставлен костыль, типа паттерна Singleton.

То есть. При объявлении переменной внутри цикла, компилятор сперва проверяет, существует ли она или нет. И если нет - то создает ее, а если она уже есть - просто обращается к ней, без изменения значения.

Сейчас проверил на C#. Этот костыль сидит только в цикле. Внутри функции такое не работает, переменная каждый раз создается заново. По крайней мере обнуляется, учитывая что адрес остается тот же. Я думаю, эта лишняя проверка замедляет работу цикла. Наверно это одна из причин, по которой всякие движки пишутся на чем-то низкоуровневом.

Windows Batch file
1
2
3
4
5
6
7
8
9
10
11
12
Адрес переменной: 367022039920 и ее значение: 0
Адрес переменной: 367022039920 и ее значение: 1
Адрес переменной: 367022039920 и ее значение: 2
Адрес переменной: 367022039920 и ее значение: 3
Адрес переменной: 367022039920 и ее значение: 4
24
Адрес переменной: 367022039920 и ее значение: 0
Адрес переменной: 367022039920 и ее значение: 1
Адрес переменной: 367022039920 и ее значение: 2
Адрес переменной: 367022039920 и ее значение: 3
Адрес переменной: 367022039920 и ее значение: 4
25
Добавлено через 14 минут
Цитата Сообщение от Storm23 Посмотреть сообщение
Это плохой код. И new здесь не самая большая проблема.
Вот я тоже как думал. Но оказывается все еще хуже.
В ООП, по крайней мере в C#, код будет "плохим" в любом случае.

Судя по моим тестам с объявлением переменной, вообще не важно где ее прописывать. Перед циклом или внутри него. Программа все-равно перепроверяет ее постоянно, как объект в singleton-е. И явно замедляет работу. А вот память как раз может и не тратится. В мое примере адрес переменной вообще не меняется.

Уверен, с объектами будет не лучше.
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
29.02.2020, 11:35
Цитата Сообщение от Серый74 Посмотреть сообщение
А я думал что как раз просил, когда писал "int variable;", но похоже что в циклы вставлен костыль, типа паттерна Singleton.
То есть. При объявлении переменной внутри цикла, компилятор сперва проверяет, существует ли она или нет. И если нет - то создает ее, а если она уже есть - просто обращается к ней, без изменения значения.
Ну что вы такое пишите? Какой синглтон? Ничего он не проверяет и не создает внутри цикла. Почитайте теорию работы компиляторов, что бы разобраться в теме.

Если сильно упрощенно, то с локальными переменными метода компилятор работает так:

1) Компилятор смотрит какие переменные объявлены в методе(независимо от их местоположения) и создает их список. Обратите внимание, что число переменных в методе - фиксированно и заранее известно, даже если переменная в цикле. Известен также и размер переменных. Если переменная является указателем на объект - то 4(или 8) байта, если структурного типа - то размер равен размеру структуры.
2) Компилятор считает суммарный размер памяти под переменные. Это возможно, потому что известно их количество и их размер.
3) При вызове метода, компилятор один раз при входе в метод резервирует кусок памяти в стеке под все переменные метода.
4) При выходе из метода, стек автоматически откатывается и память освобождается. Что делает использование переменных практически бесплатным, поскольку сборщику мусора не нужно забоится о стеке, он освободится сам.
Сборщик же мусора занимается только объектами, но не ссылками на них, которые и хранятся в переменных.

Таким образом, что вы объявите переменную внутри цикла, что снаружи - разницы абсолютно нет.
Тут главное не путать объявление переменной, и создание объекта для переменной. Это две большие разницы.
0
23 / 22 / 4
Регистрация: 25.06.2018
Сообщений: 169
29.02.2020, 12:00  [ТС]
Цитата Сообщение от Storm23 Посмотреть сообщение
Ну что вы такое пишите? Какой синглтон? Ничего он не проверяет и не создает внутри цикла. Почитайте теорию работы компиляторов, что бы разобраться в теме.
Я там оговорился, сам запутался и всех запутал. Речь вообще не про компилятор. Плевать на него, ему работать один раз.
Вопрос по работе уже скомпилированной программы. Конкретно, "new" перед циклом или внутри него - даст разницу в производительности?
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10425 / 5155 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
29.02.2020, 12:19
Лучший ответ Сообщение было отмечено Серый74 как решение

Решение

Цитата Сообщение от Серый74 Посмотреть сообщение
Конкретно, "new" перед циклом или внутри него - даст разницу в производительности?
Да, даст разницу.
Для структурных типов: при каждом new будет затрачено время на очистку и инициализацию уже выделенной памяти (в стеке). Для ссылочных типов: при new каждый раз будет выделяться память в куче и будет вызван конструктор объекта.
1
Модератор
Эксперт функциональных языков программирования
3134 / 2281 / 469
Регистрация: 26.03.2015
Сообщений: 8,877
01.03.2020, 12:31
Цитата Сообщение от Storm23 Посмотреть сообщение
Для структурных типов: при каждом new будет затрачено время на очистку и инициализацию уже выделенной памяти (в стеке). Для ссылочных типов: при new каждый раз будет выделяться память в куче и будет вызван конструктор объекта.
Судя по первому сообщению и примеру в нём, инициализация нужна в любом случае. То есть, интересует разница между new List<int>() и list.Clear(), между new DateTime() и date = default. В первом случае небольшая разница есть, но в C# её можно не учитывать. Во втором случае разницы нет.
А пример с отрисовкой из другой серии - там константа вычисляется внутри цикла.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
01.03.2020, 12:31
Помогаю со студенческими работами здесь

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

Объявление переменных в С++
Начал изучение С++ и возник такой вопрос как повторно производить действия с переменными после их объявления?

Объявление переменных
Есть. Общая форма с подчиненной. В подчиненной список. Ходим по списку, все замечательно. Как передать в главную форму ID записи из...

Объявление переменных
Простой вопрос: вывожу в консоли надпись: введите элементы через пробел, пользователь вводит.... Как объявить их в переменные... (Так...

Объявление переменных
Здравствуйте. Я создал вторую форму, в коде которой используется переменные из первой формы. Эти переменные в первой форме объявлены...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
Вывод текста со шрифтом TTF на Android с помощью библиотеки SDL3_ttf
8Observer8 25.01.2026
Содержание блога Если у вас не установлены Android SDK, NDK, JDK, и т. д. то сделайте это по следующей инструкции: Установка Android SDK, NDK, JDK, CMake и т. д. Сборка примера Скачайте. . .
Использование SDL3-callbacks вместо функции main() на Android, Desktop и WebAssembly
8Observer8 24.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
моя боль
iceja 24.01.2026
Выложила интерполяцию кубическими сплайнами www. iceja. net REST сервисы временно не работают, только через Web. Написала за 56 рабочих часов этот сайт с нуля. При помощи perplexity. ai PRO , при. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru