Я попытался улучшить производительность некоторого фрагмента кода в своем проекте, сгенерировав IL специально для этой задачи.
В настоящее время эта задача выполняется путем выполнения цикла for над элементами массива и запуска различных методов через интерфейс. Я хотел заменить его на IL, который специально выполняет эту задачу без каких-либо вызовов виртуального/интерфейса (непосредственно выполняя необходимые операции).
По какой-то причине производительность этого DynamicMethod во время выполнения намного ниже, чем производительность исходного кода, выполняющего интерфейсные вызовы для каждого элемента. Единственная причина, по которой я вижу, заключается в том, что мой DynamicMethod довольно большой (несколько инструкций на элемент массива).
Я думал, что это может быть первый медленный вызов из-за JIT, но это не так. Все вызовы медленнее. Кто-нибудь сталкивался с подобным?
изменить
Люди здесь запрашивают код ... исходный код довольно большой, но вот уменьшенная версия (это код автоматической дифференциации для вычисления градиента функции с использованием обратного режима AD). Все элементы в моем массиве наследуют следующий класс
abstract class Element
{
public double Value
public double Adjoint
public abstract void Accept(IVisitor visitor)
}
У меня есть два класса, которые происходят от element. Для простоты я определю только следующие два
class Sum : Element
{
public int IndexOfLeft; // the index in the array of the first operand
public int IndexOfRight; // the index in the array of the second operand
public abstract void Accept(IVisitor visitor) { visitor.Visit(this); }
}
class Product : Element
{
public int IndexOfLeft; // the index in the array of the first operand
public int IndexOfRight; // the index in the array of second first operand
public abstract void Accept(IVisitor visitor) { visitor.Visit(this); }
}
Вот реализация посетителя:
class Visitor : IVisitor
{
private Element[] array;
public Visitor(Element[] array) { this.array = array; }
public void Visit(Product product)
{
var left = array[product.IndexOfLeft].Value;
var right = array[product.IndexOfRight].Value;
// here we update product.Value and product.Adjoint according to some mathematical formulas involving left & right
}
public void Visit(Sum sum)
{
var left = array[sum.IndexOfLeft].Value;
var right = array[sum.IndexOfRight].Value;
// here we update sum.Value and product.Adjoint according to some mathematical formulas involving left & right
}
}
Мой исходный код выглядит так:
void Compute(Element[] array)
{
var visitor = new Visitor(array);
for(int i = 0; i < array.Length; ++i)
array[i].Accept(visitor);
}
Мой новый код пытается сделать что-то вроде этого
void GenerateIL(Element[] array, ILGenerator ilGenerator)
{
for(int i = 0; i < array.Length; ++i)
{
// for each element we emit calls that push "array[i]" and "array"
// to the stack, treating "i" as constant,
// and emit a call to a method similar to Visit in the above visitor that
// performs a computation similar to Visitor.Visit.
}
}
Затем я вызываю сгенерированный код... и он выполняется медленнее, чем двойная отправка, которую я получаю с шаблоном посетителя при вызове Compute(array);
ILGenerator
. Из комментария к последнему опубликованному вами методу видно, что вы пытаетесь повысить производительность за счет развертывания цикла. Я не верю, что это поможет вам в этом случае, если только вы не кэшируете методы по размеру массива и намерению. Генерация и последующий вызов кода для развертывания цикла (как правило) намного дороже, чем простой вызов кода для однократного прокрутки элементов в наборе. - person mlorbetske   schedule 28.09.2012