Здравствуйте, Геннадий Васильев, Вы писали: ГВ>Хорошо, пусть тогда это будет апериодический процесс, который вызывается по заполнении очередного сегмента (area). Скажи пожалуйста, как понимать выражение: "строится граф живых объектов"? AFAIK, чтобы построить такой граф, нужно пройти по ссылкам и как минимум, убедиться, что проверенные ссылки не нулевые. Или нет?
Я не согласен называть построение графа живых объектов "проверкой состояния памяти". На состояние памяти GC совершенно наплевать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: ГВ>>И они оба сильно отстают от: ГВ>>
ГВ>>for (int j = 0; j < ArrSize; ++j)
ГВ>> for (int i = 0; i < ArrSize; ++i)
ГВ>>
S>В релизе? Не из-под студии?
Угу, x64. S>Очень, очень странно. Опубликуй, пожалуйста, программу — студии под рукой нет.
См. ниже. Бинарник. S>И, да, выкинь нахрен DateTime. Пользуйся System.Threading.Timer.
Как тут поможет Timer? ГВ>>Хотя массив создаётся в той же функции и казалось бы, можно определить константную длину. S>К месту создания массива это отношения не имеет. Ресайза массивов в дотнете нет, поэтому можно закладываться на постоянство arr.Length в любом случае.
Код под катом.
Скрытый текст
using System;
namespace ArrayOnly
{
class Program
{
const int ArrSize = 50000;
const int IterCount = 5;
static void assign(long[] arr, int index, int val)
{
arr[index] = val;
}
static void testFunc2()
{
var arr = new long[ArrSize];
var dt = DateTime.Now;
for (int c = 0; c < IterCount; ++c)
for (int j = 0; j < arr.Length; ++j)
for (int i = 0; i < arr.Length; ++i)
{
assign(arr, i, i * j * c);
}
var dte = DateTime.Now;
Console.WriteLine("C# for (int i = 0; i < arr.Length; ++i): {0} ms", (dte - dt).TotalMilliseconds / IterCount);
}
static void testFunc1()
{
var arr = new long[ArrSize];
var dt = DateTime.Now;
for (int c = 0; c < IterCount; ++c)
for (int j = 0, je = arr.Length; j < je; ++j)
for (int i = 0, ie = arr.Length; i < ie; ++i)
{
assign(arr, i, i * j * c);
}
var dte = DateTime.Now;
Console.WriteLine("C# for (int i = 0, ie = arr.Length; i < ie; ++i): {0} ms", (dte - dt).TotalMilliseconds / IterCount);
}
static void testFunc()
{
var arr = new long[ArrSize];
var dt = DateTime.Now;
for (int c = 0; c < IterCount; ++c)
for (int j = 0; j < ArrSize; ++j)
for (int i = 0; i < ArrSize; ++i)
{
assign(arr, i, i * j * c);
}
var dte = DateTime.Now;
Console.WriteLine("C# for (int i = 0; i < ArrSize; ++i): {0} ms", (dte - dt).TotalMilliseconds / IterCount);
}
static void Main(string[] args)
{
testFunc();
testFunc1();
testFunc2();
System.Console.WriteLine("Press Enter to exit...");
System.Console.ReadLine();
}
}
}
Вывод (VC2010 -> x64, Win 7):
C# for (int i = 0; i < ArrSize; ++i): 1064,6609 ms
C# for (int i = 0, ie = arr.Length; i < ie; ++i): 1417,08104 ms
C# for (int i = 0; i < arr.Length; ++i): 1418,88116 ms
Press Enter to exit...
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Cyberax, Вы писали:
C>>>Хм. Написать что-ли статический проверяльщик, который ровно это делает?.. M>>Напиши Но ИМХО это пока из области научной фантастики. C>Почему? Просто достаточно запретить все небезопасные обращения. Т.е. доступ к вектору/строчке — только по at() и т.п.
Это рантаймовые проверки. То есть просадка производительности.
C>С арифметикой указателей только аккуратно надо будет, особенно с итераторами.
Ага. Вот например std::copy на итераторах. Как там проверить, что в приёмнике места достаточно? Только если заменить итераторы на диапазоны и добавить рантаймовые проверки. То есть просадка производительности. Да и с диапазонами легко накосячить, поскольку они всё равно на итераторах, которые могут инвалидироваться.
Здравствуйте, Sinclair, Вы писали:
ГВ>>Хорошо, пусть тогда это будет апериодический процесс, который вызывается по заполнении очередного сегмента (area). Скажи пожалуйста, как понимать выражение: "строится граф живых объектов"? AFAIK, чтобы построить такой граф, нужно пройти по ссылкам и как минимум, убедиться, что проверенные ссылки не нулевые. Или нет? S>Я не согласен называть построение графа живых объектов "проверкой состояния памяти". На состояние памяти GC совершенно наплевать.
Хорошо, пусть это будет "анализ состояния памяти". Хотя большой разницы нет: проанализировать и принять решение на основании анализа, считай, то же самое, что и: "проверить и принять решение на основании результата проверки". Разница в оттенках.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Sinclair, Вы писали:
ГВ>>Совершенно не обязательно. S>В тех случаях, когда это необязательно, и GC вызывать тоже необязательно.
Я бы не стал обобщать.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Скажем более общо: вызов операций удаления объектов определяется политикой управления памятью, которая, в свою очередь зависит от выбранной архитектуры, от задач приложения и кучи других соображений. Часть приложения может работать под одной политикой, часть под другой, часть — под третьей...
Все эти песни мы уже слышали в 1996 году, при появлении Java. На практике даже заменой стандартного аллокатора занимаются единичные мегагуры C++. Не говоря уже о том, чтобы заниматься поддержкой множества политик управления памятью в пределах одного приложения.
На той же практике мегагуру дотнета/джавы аналогичного уровня тоже умеют трюки с управлением памятью.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Mazay, Вы писали:
M>Ага. Вот например std::copy на итераторах. Как там проверить, что в приёмнике места достаточно? Только если заменить итераторы на диапазоны и добавить рантаймовые проверки. То есть просадка производительности. Да и с диапазонами легко накосячить, поскольку они всё равно на итераторах, которые могут инвалидироваться.
Или сделать в диапазонах compile-time проверки, введя размер в аргумент шаблона.
Не уверен, что это прожуётся современными компиляторами С++.
И что есть достаточное количество таких задач, где размеры диапазонов заранее известны.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Хорошо, пусть это будет "анализ состояния памяти". Хотя большой разницы нет: проанализировать и принять решение на основании анализа, считай, то же самое, что и: "проверить и принять решение на основании результата проверки". Разница в оттенках.
Нет никакого "решения", нет никакого "анализа".
Есть процедура "спасение живых объектов". Нет процедуры "удаление мёртвых объектов".
В классическом менеджере памяти нет процедуры "спасение живых объектов", зато есть процедура "удаление мёртвых объектов".
Этих несложных соображений пытливому уму должно быть достаточно для того, чтобы прикинуть, где должна пролегать граница между "GC эффективнее" и "классика эффективнее".
И уж тем более достаточно для того, чтобы навсегда отказаться от заблуждений вроде "классика всегда эффективнее".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Как тут поможет Timer?
DateTime катастрофически неточен.
ГВ>Код под катом.
Предлагаю следующее простое изменение:
using System;
namespace ArrayOnly
{
class Program
{
const int ArrSize = 50000;
const int IterCount = 5;
static void testFunc2()
{
var arr = new long[ArrSize];
var dt = DateTime.Now;
for (int c = 0; c < IterCount; ++c)
for (int j = 0; j < ArrSize; ++j)
for (int i = 0; i < arr.Length; ++i)
{
arr[i]= i * j * c;
}
var dte = DateTime.Now;
Console.WriteLine("C# for (int i = 0; i < arr.Length; ++i): {0} ms", (dte - dt).TotalMilliseconds / IterCount);
}
static void testFunc1()
{
var arr = new long[ArrSize];
var dt = DateTime.Now;
for (int c = 0; c < IterCount; ++c)
for (int j = 0, j < ArrSize; ++j)
for (int i = 0, ie = arr.Length; i < ie; ++i)
{
arr[i]= i * j * c;
}
var dte = DateTime.Now;
Console.WriteLine("C# for (int i = 0, ie = arr.Length; i < ie; ++i): {0} ms", (dte - dt).TotalMilliseconds / IterCount);
}
static void testFunc()
{
var arr = new long[ArrSize];
var dt = DateTime.Now;
for (int c = 0; c < IterCount; ++c)
for (int j = 0; j < ArrSize; ++j)
for (int i = 0; i < ArrSize; ++i)
{
arr[i]= i * j * c;
}
var dte = DateTime.Now;
Console.WriteLine("C# for (int i = 0; i < ArrSize; ++i): {0} ms", (dte - dt).TotalMilliseconds / IterCount);
}
static void Main(string[] args)
{
testFunc();
testFunc1();
testFunc2();
System.Console.WriteLine("Press Enter to exit...");
System.Console.ReadLine();
}
}
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
ГВ>>Скажем более общо: вызов операций удаления объектов определяется политикой управления памятью, которая, в свою очередь зависит от выбранной архитектуры, от задач приложения и кучи других соображений. Часть приложения может работать под одной политикой, часть под другой, часть — под третьей... S>Все эти песни мы уже слышали в 1996 году, при появлении Java. S>На практике даже заменой стандартного аллокатора занимаются единичные мегагуры C++. S>Не говоря уже о том, чтобы заниматься поддержкой множества политик управления памятью в пределах одного приложения.
Ну что ж, спасибо за комплимент. Надо сказать, весьма неожиданно для этого форума.
S>На той же практике мегагуру дотнета/джавы аналогичного уровня тоже умеют трюки с управлением памятью.
Не сомневаюсь.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Sinclair, Вы писали:
ГВ>>Я бы не стал обобщать. S>Однако уже обобщаешь.
Возможно.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Sinclair, Вы писали:
ГВ>>Как тут поможет Timer? S>DateTime катастрофически неточен.
В данном случае это не имеет значения, т.к. длительность измеряемого интервала — 4.5-7.5 секунд.
ГВ>>Код под катом. S>Предлагаю следующее простое изменение:
C# for (int i = 0; i < ArrSize; ++i): 890,85096 ms
C# for (int i = 0, ie = arr.Length; i < ie; ++i): 1414,08088 ms
C# for (int i = 0; i < arr.Length; ++i): 1414,88092 ms
Press Enter to exit...
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>А... Ну это-то я как раз читал. Ну, всё равно спасибо.
Пишешь так как-будто не читал.
ГВ>По умолчанию мы считаем, что рассматривается вся доступная процессу память.
Нет, так не считаем. Если заканчивается вся доступная память то уже поздно что-то делать.
G>>>>4) Чтобы программа не тормозила не надо нарушать статистику, на основе которой построены алгоритмы GC, что с завидным упорством делают программисты C++ когда пишут на C#. ГВ>>>И в чём выражаются такие нарушения? G>>Например в создании долгоживущих объектов без необходимости. Вроде создания пула объектов для уменьшения количества выделений памяти (довольно частая оптимизация в unmanaged) в managed может приводить к отрицательным результатам.
ГВ>А в чём тут отрицательное влияние выражается? В миграции ссылки на долгоживущий объект из памяти Gen2 в память Gen0?
1)Перемещение объектов в Gen2 — дополнительные затраты
2) долгоживущие объекты вызывают более частую и затратную сбоку мусора.
ГВ>Ну и потом, это не только из unmanaged, такой же подход используется в Java. Собственно, пулы хорошо решают задачу снижения издержек конструирования объектов, распределение памяти — только часть этой задачи.
Если объекты "тяжелые", то такой подход оправдан. Но сама операция выделения памяти в managed очень быстрая (по сути interlocked increment), затраты вносит сборка мусора которая зависит от количества живых объектов. Это значит что чем меньше остается живых перед сборкой мусора — тем лучше.
Здравствуйте, Sinclair, Вы писали:
ГВ>>Код под катом. S>Предлагаю следующее простое изменение:
Если все циклы по i заменить на:
for (int i = 0; i < ArrSize; ++i)
А циклы по j привести к первоначальному виду, то время для всех трёх вариантов станет одинаковым: ~885 мс.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, gandjustas, Вы писали:
ГВ>>А... Ну это-то я как раз читал. Ну, всё равно спасибо. G>Пишешь так как-будто не читал.
Я не так много работаю с .Net, чтобы нужно помнить детали работы GC. А уж в эффектах — так и вообще путаюсь, вычисляю их сугубо логически по ходу дискуссии.
ГВ>>По умолчанию мы считаем, что рассматривается вся доступная процессу память. G>Нет, так не считаем. Если заканчивается вся доступная память то уже поздно что-то делать.
O'K
Про остальное — я понял твою точку зрения. Пока что мне тут добавить нечего.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
ГВ>А циклы по j привести к первоначальному виду, то время для всех трёх вариантов станет одинаковым: ~885 мс.
Здесь самое любопытное явление — снижение производительности варианта, использующего только сравнение с константами (885 vs. 1065, т.е. ~17%). Либо JIT делает какие-то слишком далеко идущие предположения, либо... Дальше мысль останавливается.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!