Pull to refresh

Comments 24

Вопрос есть, но в сторону: известна ли Вам возможность получить IL code скомпилированного Expression Trees, желательно прямо в Visual Studio (плагин) во время исполнения? Вот эти способы stackoverflow.com/questions/4764242/viewing-the-il-code-generated-from-a-compiled-expression очень неудобные и не совместимые со стандартной библиотекой (нет Save).
Если честно, не задумывался над этим :)
В студии не знаю, а вот в windbg — запросто.
А если имелось в виду получить в самом коде, то можно посмотреть что есть в clrmd, например.
Мало того, что эти способы неудобные, так они ещё и недостоверные.
Всё дело в том, что из-за определённых моментов, связанных с безопасностью, код, генерируемый Expression<...>.Compile() и код, который генерится при создании динамической билиотеки, может оказаться разным.
Это да. Для полноты картины для археологов будующего напишу и что `CompileToMethod` даже некомпилирует во многих случаях ("'CompileToMethod cannot compile constant '..' because it is a non-trivial value, such as a live object. Instead, create an expression tree that can construct this value.'") в тех в которых `LambdaExpression.Compile` — компилирует.
Далее, если продолжить рассуждения, то что такое ref struct в понятиях Span?
Абзац, имхо, нечитабельный.

В целом, много технических особенностей реализации, но я честно так и не понял, зачем о них знать. Знать надо, когда и как применять Span, как он устроен, а не как он написан в недрах C#.
Абзац изменю, согласен. А по примерам применения — нет. Тут вся статья в примерах (например, ValueStringBuilder). А про то как он устроен, как по мне, не так и много.
Как я понял, посыл статьи, что unsafe пугает, поэтому мы спрячем unsafe за Span. Так вот Span ничем тут помочь не может, все равно, чтобы заалоцировать массив на стеке надо unsafe код, причем спрятать этот unsafe за библиотечным вызовом не получится, так как область видимости метод. Единственный способ, спрятать unsafe сейчас с помощью Span это коллбек, что довольно неуклюже.
public delegate void Callback(Span span);

public unsafe void Invoke(Callback callback) {
Span s = stackalloc double[100];
callback(s);
}


В той же Java это сделано значительно элегантнее, и там заалоцировать массив на стеке задача JVM и escape analysis.

Span КМК сейчас это просто инструмент для создания АПИ доступных как из перформансного unsafe кода, так и для "обычного" safe кода. Ну, и дешевый способ создавать легковесные слайсы, без аллокаций на хипе. Пока не введут способ без unsafe кода и unsafe struct с fixed аллоцировать на стеке, это все равно игрушка для low level кода. Нужен высокоуровневый инструмент, для работы с массивами на стеке, в Java же он есть.

То есть в java нет возможности регулировать выделение в стеке вручную?

Нет, там на уровне концепции языка нет стека практически. Java программа же не зависит от платформы, виртуальной машины итп. Она живет в виртуальном мире и сильно отвязана от конкретной среды исполнения. Но тот же new Object() может заалоцировать объект хоть в хипе, хоть на стеке, хоть вообще по регистрам раскидать его.

А как object можно зааллоцировать на стеке, если на уровне концепции языка практически нет стека?

А вас это вообще не должно волновать. Как JIT-компилятор решил, так и будет. Язык скрывает от вас такие мелочи.
Но тот же new Object() может заалоцировать объект хоть в хипе, хоть на стеке, хоть вообще по регистрам раскидать его.

www.beyondjava.net/escape-analysis-java
shipilev.net/jvm-anatomy-park/18-scalar-replacement
Вот это ваш прорыв в мире jvm?
dotnet делает это намного более предсказуемо и стабильно и делал это намного раньше чем появилось в java. Не думаю, что кто либо полагается на ваш new () аллокатор, который даже не может дать предсказуемого результата по выделении памяти в рамках метода. Как микрооптимизация, если сработало — ок, не сработало — сорри. Концептуально на проблемы работы с managed памятью это никак не повлияло — фрагментация, STW паузы и прочие прелести тормознутой managed платформы остались на месте.
Нет, там на уровне концепции языка нет стека практически. Java программа же не зависит от платформы, виртуальной машины итп.

В том то и дело, все дело в языке — если он не имеет апи для поддержки работы с памятью это ограниченность языка, комплилятора и рантайма — не более, а не ограничение наложенной кросплатформенной моделью разработки.
А что работа со стеком платформо зависимая штука или с памятью? Или по вашему программа написанная на С выделяет память на стеке по разному под виндой и линуксом?

Вообще-то, да.
Есть разные соглашения о вызовах функций.
Есть куча процессорных архитектур (x86, x64, ARM, ARM64), в т.ч. с экзотикой в виде little-endian/big-endian.
И Java скрывает все эти нюансы от программиста.


Посмотрите на кросс-платформенный C++ код — он кишит платформо-зависимыми ifdef-ами, которых в Java нет.


Все дело в языке — если он не имеет апи для поддержки работы с памятью это ограниченность языка, комплилятора и рантайма — не более.

Это ограниченность by design.

Это ограниченность by design.

В чем проблема спрятать эти ifdef в реализации jvm и добавить api в java? Все дело в реализации рнтайма, компилятора, обратной совместимости и языковом дизайне, да и только.

Дело не только в стеке, но и в возможности работать c native memory не задействуя механизмы CLR и значительно оптимизируясь механизмы выделения памяти, освобождения. в .net нам это апи переписаны многие стандартные net framework вещи — работа с сокетами, parsing api, работа с потоками(TPL) и т.д как результат интенсивные вычисления чувствительные к аллокациям даже из высокоуровневого api делают с производительностью native кода


https://www.codeproject.com/Articles/1223361/Benchmarking-NET-Core-SIMD-performance-vs-Intel-IS


Для Java это недосягаемые вершины на текущий момент.
Кстати можно почитать, что Java умеет делать с массивами на стеке без вмешательства девелопера?

Единственный способ, спрятать unsafe сейчас с помощью Span это коллбек, что довольно неуклюже.
public delegate void Callback(Span span);

public unsafe void Invoke(Callback callback) {
Span s = stackalloc double[100];
callback(s);
}


Еще можно вот так и в отлчии от Java аллокатора дает гарантировнный результат
	
                static void Main(string[] args)
		{
			var array = "1,3,5,".AsSpan();
			var separator = ",".AsSpan();
			Span<int> span = stackalloc int[3];

			int idx, parsed = 0;
			while ((idx = array.IndexOf(separator)) != -1)
			{
				var val = int.Parse(array.Slice(0, idx));
				array = array.Slice(idx + 1);
				span[parsed++] = val;
			}

			var res = 0;
			foreach (var i in span)
			{
				res += i;
			}

			Console.WriteLine(res);
			Console.ReadLine();
		}


unsafe не нужен при работе со стеком.
Причём здесь D?
Или вы думаете, что D — это первый язык, в котором появились слайсы?
Большое спасибо за грамотную статью про Span., очень хорошо и понятно пишете!
Всегда слежу за новыми фичами c# и изучаю все по мере выхода. Когда появился Span, было сложно сходу понять что он делает, а времени эксперементировать самому как всегда нет) Прочитал много статей, но только после этой все понял и вопрос можно считать закрытым.

есть пару вопросов
1) пока читал статью ожидал упоминания ArraySegment и так его и не встретил. Видел использование этого класса в системных апи с сокетом, а в .net Core 1.x только они и были доступны — хотелось бы раскрытия вопроса, решает-ли Span проблемы которые решал ArraySegment, и что теперь использовать.

2) Поправьте меня если не прав — машина тьюринга описывает программирование на стеке, и куча не является необходимым элементом для создания тьюринг полных языков/программ, она существует чтобы было удобно строить абстракции. Глобальный вопрос состоит в том, возможно-ли использовать силу абстракции ООП и при этом остаться полностью на стеке, ну или хотя бы в той мере, чтобы куча не так сильно влияла на производительность. Самому пока чего-то не хватает чтобы разобраться.
Sign up to leave a comment.