Jak powolne jest odbicie

Niedawno stworzyłem warstwę interfejsu, aby odróżnić DataAccessProvider od naszej warstwy logiki biznesowej. Dzięki temu podejściu możemy zmienić nasz wybór DataAccessProvider, kiedy tylko chcemy, zmieniając wartości w Web/App.Config. (w razie potrzeby można podać więcej szczegółów).

W każdym razie, aby to zrobić, używamy reflection, aby wykonać naszą klasę DataProvider, na której możemy pracować.

/// <summary>
/// The constructor will create a new provider with the use of reflection.
/// If the assembly could not be loaded an AssemblyNotFoundException will be thrown.
/// </summary>
public DataAccessProviderFactory()
{
    string providerName = ConfigurationManager.AppSettings["DataProvider"];
    string providerFactoryName = ConfigurationManager.AppSettings["DataProviderFactory"];
    try
    {
        activeProvider = Assembly.Load(providerName);
        activeDataProviderFactory = (IDataProviderFactory)activeProvider.CreateInstance(providerFactoryName);
    }
    catch
    {
        throw new AssemblyNotFoundException();
    }
}
Ale teraz zastanawiam się, jak powolne jest odbicie?
Author: Marc Gravell, 2009-04-21

7 answers

W większości przypadków: więcej niż wystarczająco szybko. Na przykład, jeśli używasz tego do tworzenia obiektu wrappera DAL, czas potrzebny na utworzenie obiektu przez odbicie będzie minuskułą w porównaniu do czasu, jaki musi on połączyć się z siecią. Optymalizacja tego byłaby więc stratą czasu.

Jeśli używasz odbicia w ciasnej pętli, istnieją sztuczki, aby je poprawić:

  • generics (using a wrapper where T : new() and MakeGenericType)
  • Delegate.CreateDelegate (do wpisanego delegata; nie praca dla konstruktorów)
  • Reflection.Emit - hardcore
  • Expression (podobnie jak Delegate.CreateDelegate, ale bardziej elastyczny i działa dla konstruktorów)
Ale dla Twoich celów, jest w porządku. Trzymaj się tego i zachowaj prostotę.

Edit: podczas gdy kwestia względnej wydajności pozostaje, i podczas gdy najważniejsza rzecz," zmierz ją", pozostaje, powinienem wyjaśnić niektóre z powyższych. Czasami... to ma znaczenie. Najpierw zmierz. Jeśli jednak znajdziesz jest zbyt wolny, warto spojrzeć na coś w rodzaju FastMember , który robi cały kod Reflection.Emit cicho w tle, aby dać ci ładne proste API; na przykład:

var accessor = TypeAccessor.Create(type);
List<object> results = new List<object>();
foreach(var row in rows) {
    object obj = accessor.CreateNew();
    foreach(var col in cols) {
        accessor[obj, col.Name] = col.Value;
    }
    results.Add(obj);
}

Co jest proste, ale będzie bardzo szybkie. W konkretnym przykładzie wspominam o owijarce DAL-jeśli robisz to dużo, rozważ coś w stylu dapper , który ponownie robi cały kod Reflection.Emit w tle, aby dać Ci najszybsze możliwe, ale łatwe w użyciu API:

int id = 12345;
var orders = connection.Query<Order>(
    "select top 10 * from Orders where CustomerId = @id order by Id desc",
    new { id }).ToList();
 81
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-31 21:26:30

Jest wolniejszy w porównaniu do kodu nierefleksyjnego. Ważne nie jest, czy jest powolny, ale jeśli jest powolny Gdzie się liczy . Na przykład, jeśli tworzysz instancje obiektów za pomocą odbicia w środowisku internetowym, w którym oczekiwana koncentracja może wzrosnąć do 10K, będzie to powolne.

W każdym razie, dobrze jest nie martwić się o wydajność z góry. Jeśli rzeczy okażą się powolne, zawsze możesz je przyspieszyć, jeśli zaprojektowałeś rzeczy poprawnie, aby części, których się spodziewałeś, mogły być potrzebne optymalizacja w przyszłości jest zlokalizowana.

Możesz sprawdzić ten słynny artykuł, jeśli potrzebujesz przyspieszyć:

Dynamiczny... Ale szybko: opowieść o trzech małpach, wilku i klasach DynamicMethod i ILGenerator

 19
Author: majkinetor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-06 19:24:39
 9
Author: Ruben Steins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-21 08:06:49

Odbicie nie jest takie powolne. Wywołanie metody przez odbicie jest około 3 razy wolniejsze niż normalny sposób. Nie ma problemu, jeśli zrobisz to tylko raz lub w sytuacjach niekrytycznych. Jeśli użyjesz go 10 ' 000 razy w metodzie krytycznej czasowo, rozważyłbym zmianę implementacji.

 5
Author: Enyra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-21 08:36:58

Pomyślałem, że zrobię szybki test, aby pokazać, jak powolne odbicie jest w porównaniu z bez.

Z Odbiciem

  • Tworzenie instancji 58 obiektów poprzez iterację każdego z ich atrybutów i dopasowanie
  • Całkowity Czas: 52254 nanosekund

    while (reader.Read()) {
        string[] columns = reader.CurrentRecord;
        CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry();
        IEnumerable<PropertyInfo> rawPayFileAttributes = typeof(CdsRawPayfileEntry).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(CustomIndexAttribute)));
        foreach (var property in rawPayFileAttributes) {
            int propertyIndex = ((CustomIndexAttribute)property.GetCustomAttribute(typeof(CustomIndexAttribute))).Index;
            if (propertyIndex < columns.Length)
                property.SetValue(toReturn, columns[propertyIndex]);
            else
                break;
        }
    }
    

Bez Refleksji

  • utworzenie instancji 58 obiektów przez utworzenie nowego obiektu
  • Całkowity Czas: 868 nanosekundy

        while (reader2.Read()) {
            string[] columns = reader2.CurrentRecord;
            CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry() {
                ColumnZero = columns[0],
                ColumnOne = columns[1],
                ColumnTwo = columns[2],
                ColumnThree = columns[3],
                ColumnFour = columns[4],
                ColumnFive = columns[5],
                ColumnSix = columns[6],
                ColumnSeven = columns[7],
                ColumnEight = columns[8],
                ColumnNine = columns[9],
                ColumnTen = columns[10],
                ColumnEleven = columns[11],
                ColumnTwelve = columns[12],
                ColumnThirteen = columns[13],
                ColumnFourteen = columns[14],
                ColumnFifteen = columns[15],
                ColumnSixteen = columns[16],
                ColumnSeventeen = columns[17]
            };
        }
    

Choć nie do końca sprawiedliwe, ponieważ odbicie musi również uzyskać określony atrybut każdej właściwości 58*18 razy na szczycie tworzenia nowego obiektu poprzez odbicie, ale przynajmniej zapewnia pewną perspektywę.

 5
Author: Levi Fuller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-02 22:59:18

Poza podążaniem za linkami podanymi w innych odpowiedziach i upewnieniem się, że nie piszesz "patalogicznie złego" kodu, to dla mnie najlepszą odpowiedzią na to jest przetestowanie go samemu.

Tylko Ty wiesz gdzie jesteś, ile razy Twój kod odbicia będzie używany, czy kod odbicia będzie w ciasnych pętlach itp. Znasz swoją sprawę biznesową, ilu użytkowników uzyska dostęp do twojej witryny, jakie są wymagania dotyczące perf.

Jednak biorąc pod uwagę fragment kodu, który pokazałeś tutaj zgaduję, że narzut refleksji nie będzie ogromnym problemem.

VS.NET testy sieciowe i funkcje testowania wydajności powinny sprawić, że pomiar wydajności tego kodu będzie dość prosty.

Jeśli nie używasz reflection, jak będzie wyglądał Twój kod? Jakie ograniczenia będzie miał? Może się okazać, że nie możesz żyć z ograniczeniami, z którymi się znajdujesz, jeśli usuniesz kod odbicia. Może warto spróbować zaprojektować ten kod bez refleksja, aby zobaczyć, czy to możliwe, czy alternatywa jest pożądana.

 3
Author: Martin Peck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-21 08:19:42

Robiłem coś podobnego, dopóki nie zacząłem grać z IoC. Użyłbym definicji obiektu Spring, aby określić dostawcę danych-SQL, XML lub Mocks!

 0
Author: n8wrl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-24 20:42:43