[Delegate].CreateDelegate для установки производительности свойства

Это сериализатор, над которым я работал, который сериализует данные в объекты на основе некоторых атрибутов сопоставленных свойств. Я прочитал несколько постов, связанных с использованием [Delegate].CreateDelegate для увеличения скорости, поэтому я попробовал. Кажется, что он получил некоторое повышение производительности, но при этом минимальное (даже после первоначального зацикливания всех типов объектов для кэширования всех возможных объектов информации о свойствах и тому подобного). Информация о сопоставленных свойствах просто имеет альтернативное имя столбца БД, которое можно назначить, потому что мы не имеем полного контроля над всеми именами столбцов, которые я использую. Это также только захватывает свойства, которые я хочу отобразить.

Прямо сейчас сериализация ок. 1500 объектов со средним числом 13 свойств на объект и средним числом 3 объекта в каждом из 1500 родительских объектов (как дочерние объекты) также заполняются в среднем 13 свойствами на штуку - вот для сравнения:

установка свойства property.Name = row.Item вручную ("columnName")
785 мс

p.SetValue
1490 мс

GetSetMethod.Invoke
1585 мс

[Делегат].CreateDelegate
1285 мс

Кто-нибудь может предложить что-то еще, что я могу сделать, чтобы увеличить производительность??

  Friend Class DataSerializer(Of T As Class)

        Private Shared MappedPropertiesCache As New Dictionary(Of Type, PropertyInfo())
        Private Shared MappedColumnNameCache As New Dictionary(Of Type, List(Of String))

        Private Shared ActionCache As New Dictionary(Of String, Action(Of T, Object))

        Friend Shared Function SerializeDataRow(ByVal row As DataRow) As T

            Dim target As Object = Activator.CreateInstance(GetType(T))

            Dim targetType As Type = GetType(T)

            AnalyzeMappedCache(target, targetType)

            Dim index As Integer = 0

            Dim mappedColumns As List(Of String) = MappedColumnNameCache.Item(targetType)

            'iterate through target object properties
            For Each p As PropertyInfo In MappedPropertiesCache.Item(targetType)

                If row.Table.Columns.Contains(mappedColumns.Item(index)) Then


                    '' SLOW
                    'p.SetValue(target, CheckDbNull(row.Item(mappedColumns.Item(index))), Nothing)


                    '' SLOWER
                    'Dim methodInfo As MethodInfo = p.GetSetMethod()

                    'methodInfo.Invoke(target, New Object() {CheckDbNull(row.Item(mappedColumns.Item(index)))})



                    ''FASTER THAN PREVIOUS TWO, BUT STILL SLOW


                    'Dim key As String = String.Format("{0}:{1}", target.GetType.FullName, p.Name)

                    'If Not ActionCache.ContainsKey(key) Then

                    '    Dim methodAction As Action(Of T, Object) = MagicMethod(p.GetSetMethod())

                    '    ActionCache.Add(key, methodAction)

                    'End If

                    'Dim param As Object = CheckDbNull(row.Item(mappedColumns.Item(index)))

                    'If Not param Is Nothing Then

                    '    ActionCache(key)(target, param)

                    'End If

                End If

                index = index + 1

            Next

            Return target

        End Function


        Private Shared Function MagicMethod(method As MethodInfo) As Action(Of T, Object)
            ' First fetch the generic form
            Dim genericHelper As MethodInfo = GetType(DataSerializer(Of T)).GetMethod("MagicMethodHelper", BindingFlags.[Static] Or BindingFlags.NonPublic)

            ' Now supply the type arguments
            Dim constructedHelper As MethodInfo = genericHelper.MakeGenericMethod(GetType(T), method.GetParameters()(0).ParameterType)

            ' Now call it. The null argument is because it's a static method.
            Dim ret As Object = constructedHelper.Invoke(Nothing, New Object() {method})

            ' Cast the result to the right kind of delegate and return it
            Return DirectCast(ret, Action(Of T, Object))
        End Function

        Private Shared Function MagicMethodHelper(Of TTarget As Class, TParam)(method As MethodInfo) As Action(Of TTarget, Object)
            ' Convert the slow MethodInfo into a fast, strongly typed, open delegate
            Dim func As Action(Of TTarget, TParam) = DirectCast([Delegate].CreateDelegate(GetType(Action(Of TTarget, TParam)), method), Action(Of TTarget, TParam))

            ' Now create a more weakly typed delegate which will call the strongly typed one
            Dim ret As Action(Of TTarget, Object) = Sub(target As TTarget, param As Object) func(target, CType(param, TParam))
            Return ret
        End Function

        Private Shared Sub AnalyzeMappedCache(ByVal target As Object, ByVal targetType As Type)

            'this assumes the target object inherits from BaseProperties
            If Not MappedPropertiesCache.ContainsKey(targetType) Then

                Dim props As PropertyInfo() = target.GetMappedProperties()

                Dim mappedColumnNameList As New List(Of String)

                For Each prop As PropertyInfo In props

                    mappedColumnNameList.Add(CType(prop.GetCustomAttributes(GetType(DTO_POMGMT.MappedProperty), True).FirstOrDefault, DTO_POMGMT.MappedProperty).ColumnName)

                Next

                MappedColumnNameCache.Add(targetType, mappedColumnNameList)

                MappedPropertiesCache.Add(targetType, props)

            End If

        End Sub

        'check for a dbnull value of any object type returned from database
        Private Shared Function CheckDbNull(ByVal obj As Object) As Object

            Return If(obj Is DBNull.Value, Nothing, obj)

        End Function

    End Class

1 ответ

Решение

Я бы посоветовал вам взглянуть на FastMember либо напрямую, либо просто позаимствовать и адаптировать код. В частности (немного переходя на C#):

var accessor = TypeAccessor.Create(targetType); 
foreach(PropertyInfo p in MappedPropertiesCache.Item(targetType))
{
    ...
    accessor[target, p.Name] = ... // newValue
    ...
}

В качестве альтернативы рассмотрим, как что-то вроде dapper-dot-net обрабатывает посимвольное назначение.

В качестве примечания: статический словарь не является потокобезопасным, и вы можете быть осторожны при обращении к нему.

Другие вопросы по тегам