Подтвердить что ты не робот

Огромное снижение производительности С#, присвоивающее значение float

Я пытаюсь оптимизировать свой код и на нем работает монитор производительности VS.

enter image description here

Это показывает, что простое назначение float занимает основную часть вычислительной мощности? Я не понимаю, как это возможно.

Вот код для TagData:

public class TagData
{
    public int tf;
    public float tf_idf;
}

Итак, все, что я действительно делаю, это:

float tag_tfidf = td.tf_idf;

Я смущен.

4b9b3361

Ответ 1

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

Точки для проверки этой теории:

  • Являются ли ваши данные большими? Ставка на это.
  • Вы получаете доступ к TagData в порядке произвольной памяти? Бьюсь об заклад, они не последовательны в памяти. Это приводит к дисфункции памяти prefetcher CPU.
  • Добавьте новую строку int dummy = td.tf; перед дорогой линией. Эта новая строка теперь будет самой дорогой линией, потому что она вызовет пропущенную кеш. Найдите способ выполнить операцию фиктивной загрузки, которую JIT не оптимизирует. Возможно, добавьте все значения td.tf в локальное и передайте это значение в GC.KeepAlive в конце метода. Это должно поддерживать загрузку памяти в JIT-излучаемом x86.

Возможно, я ошибаюсь, но, вопреки другим теориям, до сих пор мой тестируемый.

Попробуйте сделать TagData a struct. Это сделает все элементы term.tags последовательными в памяти и даст вам хороший прирост производительности.

Ответ 2

Используете ли вы LINQ? Если это так, LINQ использует ленивое перечисление, поэтому в первый раз, когда вы получите доступ к стоимости, которую вы вытащили, это будет болезненно.

Если вы используете LINQ, вызовите ToList() после запроса, чтобы заплатить только один раз.

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

EDIT: как отмечают комментаторы, LINQ, возможно, не виноват; однако мой вопрос основан на том, что оба оператора foreach используют IEnumerable. Назначение TagData является указателем на элемент в коллекции IEnumerable (который может быть или не быть перечислены еще). Первый доступ к законным данным - это строка, которая извлекает свойство из объекта. В первый раз это случается, он может выполнять весь оператор LINQ, и поскольку профилирование использует среднее значение, оно может быть выключено. То же самое можно сказать и о tagScores (который, как я предполагаю, поддерживается базой данных), чей первый доступ очень медленный, а затем ускоряется. Я не указывал на решение только возможной проблемы, учитывая мое понимание IEnumerable.

См. http://odetocode.com/blogs/scott/archive/2008/10/01/lazy-linq-and-enumerable-objects.aspx

Ответ 3

Как мы видим, следующая строка подозрительной принимает только 0.6 i.e

float tag_tfidf = td.tf_idf;//29.6
string tagName =...;//0.6

Я подозреваю, что это вызвано избыточным количеством вызовов, а также примечание float - это тип значения, то есть они копируются по значению. Поэтому каждый раз, когда вы его назначаете, среда выполнения создает новую структуру float (Single) и инициализирует ее, копируя значение из td.tf_idf, которое занимает огромное время.

Вы можете видеть, что string tagName =...; не требует многого, потому что он копируется по ссылке.

Изменить: Как отмечалось в комментариях, я могу ошибаться в этом отношении, это может быть ошибкой в ​​профилировщике, попробуйте перепрофилировать и посмотреть, не имеет значения.