Re[23]: Не понимая и половины
От: vdimas Россия  
Дата: 25.03.17 13:49
Оценка:
Здравствуйте, samius, Вы писали:

S>Я уж не знаю, как тебе объяснить, что бы ты понял.


Никак. Потому что ты выдаешь своё упрямство за якобы непонимание окружающих.

Просто прими как факт — ты говоришь простые вещи, которые понятны сходу даже новичкам. Даже студентам.
Значит, это не я тебя не понимаю, а ты меня.


S>При твоем способе вызова нет никакой структуры, реализуюющей IEnumerator. foreach не видит метод, возвращающий структуру. Он может оперировать лишь методом, который указан в ограничении. А тот возвращает класс, реализующий интерфейс.


Это так написано в LI-коде метода-генерика, что НЕ является аргументом в этом споре.
Собсно, положа руку на — это махровое нубство, аппелировать к таким вещам, когда про вэльютипы достоверно известно, что джит генерит уникальный код для каждого такого типа в рантайм.


S>Это ты мог видеть в тестах, где foreach оперирует явно методом GetEnumerator(), возвращающим структуру.


Это я видел в тестах, где возвращается класс.

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


S>В случае вызова метода GetEnumerator() структуры-параметра call, будет прямой метод.


Сие означает, что джит может "проинлайнить" прямой метод. А значит он может "увидеть" реальный возвращаемый тип, еще ДО боксинга возвращаемого значения. Затем он увидит, что отбоксированное значение не покидает scope, поэтому операцию бокирования можно "сократить в уме". Но тебе такие "сложности" не даются, вестимо. ))

И мне даже несколько пофик, умеет ли он это делать на сейчас или нет. Это вопрос десятый, потому что до RyuJit прошлые версии джита тоже много чего не делали. Я лишь пытался заставить тебя вместо тысячи слов дать сюда ассемблерный листинг, потому что вы, здешние дотнетовцы, ленивы как стадо ленивцев, отсюда имеете репутацию скучных собеседников на сайте. ))
Re[27]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.03.17 16:45
Оценка:
Здравствуйте, samius, Вы писали:


S>>То что это не дженерик интерфейс


S>Не дженерик интерфейс появился еще раньше в первом дотнете. В 2005м появились констрейнты, с помощью которых можно было подавать в генерик методы реализации (в том числе структурами) интерфейсов (и не дженерик в том числе) в дженерик методы.


S>Так что нового в этой статье-то?


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

Причина в том, что компилятор теперь имеет достаточно информации, чтобы понять, что код для Foo, и вызов интерфейса попадет именно на Foo.IAdd.Add , поэтому он пропускает формальность и вызывает функцию напрямую. Это как оптимизация производительности, но также с заметным побочным эффектом.


Возможно это было и предыдущих JIT ах.
и солнце б утром не вставало, когда бы не было меня
Re[28]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 18:03
Оценка: 27 (1) +1
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>Так что нового в этой статье-то?


S> Для меня было открытием, что констрейнт типа интерфейса, позволяет вызывать метод не через таблицу интерфейсов, а сразу нужный метод.

S>При этом для валуе типов нет боксинга.

CSharp 2.0 Specification_Sept_2005.doc (20.7.3)

When a struct type overrides a virtual method inherited from System.Object (such as Equals, GetHashCode, or ToString), invocation of the virtual method through an instance of the struct type doesn’t cause boxing to occur. This is true even when the struct is used as a type parameter and the invocation occurs through an instance of the type parameter type.
...

Similarly, boxing never implicitly occurs when accessing a member on a constrained type parameter. For example, suppose an interface ICounter contains a method Increment which can be used to modify a value. If ICounter is used as a constraint, the implementation of the Increment method is called with a reference to the variable that Increment was called on, never a boxed copy.

using System;
interface ICounter
{
    void Increment();
}
struct Counter: ICounter
{
    int value;
    public override string ToString() {
        return value.ToString();
    }
    void ICounter.Increment() {
        value++;
    }
}
class Program
{
    static void Test<T>() where T: ICounter, new() {
        T x = new T();
        Console.WriteLine(x);
        x.Increment();                        // Modify x
        Console.WriteLine(x);
        ((ICounter)x).Increment();        // Modify boxed copy of x
        Console.WriteLine(x);
    }
    static void Main() {
        Test<Counter>();
    }
}


The first call to Increment modifies the value in the variable x. This is not equivalent to the second call to Increment, which modifies the value in a boxed copy of x. Thus, the output of the program is:
0
1
1
Re[29]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.03.17 19:15
Оценка:
Здравствуйте, samius, Вы писали:

Большое спасибо! Не знал
и солнце б утром не вставало, когда бы не было меня
Re[24]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 20:17
Оценка: +1
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>Я уж не знаю, как тебе объяснить, что бы ты понял.


V>Никак. Потому что ты выдаешь своё упрямство за якобы непонимание окружающих.


V>Просто прими как факт — ты говоришь простые вещи, которые понятны сходу даже новичкам. Даже студентам.

V>Значит, это не я тебя не понимаю, а ты меня.
Я же не скрываю, что тебя не понимаю. Я ведь спрашиваю.


S>>При твоем способе вызова нет никакой структуры, реализуюющей IEnumerator. foreach не видит метод, возвращающий структуру. Он может оперировать лишь методом, который указан в ограничении. А тот возвращает класс, реализующий интерфейс.


V>Это так написано в LI-коде метода-генерика, что НЕ является аргументом в этом споре.

V>Собсно, положа руку на — это махровое нубство, аппелировать к таким вещам, когда про вэльютипы достоверно известно, что джит генерит уникальный код для каждого такого типа в рантайм.
Ну вот только прочитав твое сообщение до конца, я понял, что ты говоришь не о сценарии с ImmutableArray<T>, а о каком-то другом сценарии. Т.е. ты не рассчитываешь что будет вызван метод, возвращающий структуру. Ты рассчитываешь на то, что структура будет возвращена обычным методом, реализующим IEnumerator<T> GetEnumerator(). Ты же об этом не написал нигде. Но я-то полагал что ты рассчитываешь на то что джит вызовет метод, возвращающий структуру явно.


S>>Это ты мог видеть в тестах, где foreach оперирует явно методом GetEnumerator(), возвращающим структуру.


V>Это я видел в тестах, где возвращается класс.


V>Т.е., фишка в том, что джит оперировал знаниями о внутреннем устройстве типа, а так же "видел" время жизни неких данных, т.е. продемонстрировал способность делать некие предположения о данных и использовать эти предположения для оптимизации.

Не вопрос. Я был против того что бы джит подменял метод.


S>>В случае вызова метода GetEnumerator() структуры-параметра call, будет прямой метод.


V>Сие означает, что джит может "проинлайнить" прямой метод. А значит он может "увидеть" реальный возвращаемый тип, еще ДО боксинга возвращаемого значения. Затем он увидит, что отбоксированное значение не покидает scope, поэтому операцию бокирования можно "сократить в уме". Но тебе такие "сложности" не даются, вестимо. ))


Видишь ли, я даже подумать не мог, что ты отклонился от темы с ImmutableArray<T>, List<T> и других реализаций паттерна для foreach, которые возвращают структуру паблик методом, не имеющим отношение к интерфейсу. И подумать не мог о том, что ты решил замутить "оптимизаци" за счет пессимизации основного сценария использования перечисления, что у тебя метод, возвращающий IEnumerable<T> в качестве результата будет выдавать ValueType, который при прямом использовании через IEnumerable<T> неминуемо отбоксится. Откуда ж я мог знать? Предупреждать надо о таких вещах, а не подразумевать их.

V>И мне даже несколько пофик, умеет ли он это делать на сейчас или нет. Это вопрос десятый, потому что до RyuJit прошлые версии джита тоже много чего не делали. Я лишь пытался заставить тебя вместо тысячи слов дать сюда ассемблерный листинг, потому что вы, здешние дотнетовцы, ленивы как стадо ленивцев, отсюда имеете репутацию скучных собеседников на сайте. ))

Не ленивее тебя.
Re[30]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 20:17
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>Большое спасибо! Не знал


Не за что.
Re[25]: Не понимая и половины
От: vdimas Россия  
Дата: 26.03.17 08:04
Оценка:
Здравствуйте, samius, Вы писали:

S>Ну вот только прочитав твое сообщение до конца, я понял, что ты говоришь не о сценарии с ImmutableArray<T>, а о каком-то другом сценарии. Т.е. ты не рассчитываешь что будет вызван метод, возвращающий структуру.


Ну да.


S>Ты рассчитываешь на то, что структура будет возвращена обычным методом, реализующим IEnumerator<T> GetEnumerator(). Ты же об этом не написал нигде. Но я-то полагал что ты рассчитываешь на то что джит вызовет метод, возвращающий структуру явно.


У нас тут происходит путаница м/у "джит вызовет" и "написано в IL".
В IL будет написан вызов интерфейса.

Мой поинт в том, что если джит инлайнит вызов GetEnumerator() у некоего T : IEnumerable<T>, где, в свою очередь, опять возвращается структура, то ему "видна" операция боксинга этой структуры, реализующей IEnumerator<>. Т.е. джиту виден полный цикл создаваемой через боксинг копии объекта, т.к. эта копия живет аккурат внутри цикла и далее никуда не передаётся и никуда не возвращается. Для уверенности в этом нужно заинлайнить методы такой структуры. Там есть ограничение на сложность инлайна, но в сценарии с итераторами там код из единиц строк обычно, т.е. самый востребованный для таких вещей сценарий одновременно является и самым простым..


S>Видишь ли, я даже подумать не мог, что ты отклонился от темы с ImmutableArray<T>, List<T> и других реализаций паттерна для foreach, которые возвращают структуру паблик методом, не имеющим отношение к интерфейсу.


А какая разница?
Даже взять такую цепочку методов:
struct MyList<T> : IEnumerable<T> {
  struct MyEnumerator : IEnumerator<T> {
  ...
  }

  public MyEnumerator GetEnumerator() {
    return new MyEnumerator(_myPrivateData);
  }
 
  IEnumerator<T> IEnumerable<T>.GetEnumerator() {
    return GetEnumerator();
  }
  ...
}

Я так думаю, что вот эту цепочку из двух методов вполне можно заинлайнить, ведь класс MyList — он final, то бишь sealed.

Т.е. при генерации джитом уникального тела для нашего T=MyList<int>:
static int SumOfInts<T>(T collection) : where T : IEnumerable<T> {
  int s = 0;
  foreach(var i in collection)
    s += i;
  return s;
}

у джита достоверно есть вся информация о том, что именно будет возвращено при вызове collection.Ienumerable<T>.GetEnumerator().

Другое дело — воспользовались ли такими знаниями разработчики джита? Вот в чем был вопрос.
В общем проверил — нихрена не воспользовались.
Создаётся боксированная копия структуры и в теле цикла идёт косвенный вызов методов MoveNext и get_Current.


S>И подумать не мог о том, что ты решил замутить "оптимизаци" за счет пессимизации основного сценария использования перечисления, что у тебя метод, возвращающий IEnumerable<T> в качестве результата будет выдавать ValueType, который при прямом использовании через IEnumerable<T> неминуемо отбоксится.


Почему "неминуемо-то"?
Я прекрасно вижу, как на 2-х if расписать логику джита в этой ситуации, чтобы он не боксил ничего.
Re[26]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.03.17 18:08
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>Видишь ли, я даже подумать не мог, что ты отклонился от темы с ImmutableArray<T>, List<T> и других реализаций паттерна для foreach, которые возвращают структуру паблик методом, не имеющим отношение к интерфейсу.


V>А какая разница?

V>Даже взять такую цепочку методов:
V>
V>struct MyList<T> : IEnumerable<T> {
V>  struct MyEnumerator : IEnumerator<T> {
V>  ...
V>  }

V>  public MyEnumerator GetEnumerator() {
V>    return new MyEnumerator(_myPrivateData);
V>  }
 
V>  IEnumerator<T> IEnumerable<T>.GetEnumerator() {
V>    return GetEnumerator();
V>  }
V>  ...
V>}
V>


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

V>Я так думаю, что вот эту цепочку из двух методов вполне можно заинлайнить, ведь класс MyList — он final, то бишь sealed.


V>у джита достоверно есть вся информация о том, что именно будет возвращено при вызове collection.Ienumerable<T>.GetEnumerator().


В 99% случаев у него будет информация что это IEnumerator<T>, т.к. TColl будет в точности IEnumerable<T>.

V>Другое дело — воспользовались ли такими знаниями разработчики джита? Вот в чем был вопрос.

V>В общем проверил — нихрена не воспользовались.
V>Создаётся боксированная копия структуры и в теле цикла идёт косвенный вызов методов MoveNext и get_Current.


S>>И подумать не мог о том, что ты решил замутить "оптимизаци" за счет пессимизации основного сценария использования перечисления, что у тебя метод, возвращающий IEnumerable<T> в качестве результата будет выдавать ValueType, который при прямом использовании через IEnumerable<T> неминуемо отбоксится.


V>Почему "неминуемо-то"?

Потому что там где нужен интерфейс, структура напрямую не пролезет.

V>Я прекрасно вижу, как на 2-х if расписать логику джита в этой ситуации, чтобы он не боксил ничего.


from x in myColl select x*x

ы?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.