Как определить присутствует ли операция boxing в c#?
В общем есть такой код:
public class AccountProcessor
{
// ToDo Реализовать без копирования и боксинга
public decimal Calculate(BankAccount bankAccount)
{
return CalculateOperation(bankAccount.LastOperation) +
CalculateOperation(bankAccount.PreviousOperation) +
CalculateOperation1(bankAccount.LastOperation) +
CalculateOperation1(bankAccount.PreviousOperation) +
CalculateOperation2(bankAccount.LastOperation) +
CalculateOperation2(bankAccount.PreviousOperation) +
CalculateOperation3(bankAccount.LastOperation) +
CalculateOperation3(bankAccount.PreviousOperation) +
CalculateOperation3(bankAccount)
+
CalculateOperation(bankAccount.LastOperation) +
CalculateOperation(bankAccount.PreviousOperation) +
CalculateOperation1(bankAccount.LastOperation) +
CalculateOperation1(bankAccount.PreviousOperation) +
CalculateOperation2(bankAccount.LastOperation) +
CalculateOperation2(bankAccount.PreviousOperation) +
CalculateOperation3(bankAccount.LastOperation) +
CalculateOperation3(bankAccount.PreviousOperation) +
CalculateOperation3(bankAccount)
+
CalculateOperation(bankAccount.LastOperation) +
CalculateOperation(bankAccount.PreviousOperation) +
CalculateOperation1(bankAccount.LastOperation) +
CalculateOperation1(bankAccount.PreviousOperation) +
CalculateOperation2(bankAccount.LastOperation) +
CalculateOperation2(bankAccount.PreviousOperation) +
CalculateOperation3(bankAccount.LastOperation) +
CalculateOperation3(bankAccount.PreviousOperation) +
CalculateOperation3(bankAccount);
}
private decimal CalculateOperation(BankOperation bankOperation)
{
// Some calculation code
return bankOperation.OperationInfo0;
}
private decimal CalculateOperation1(BankOperation bankOperation)
{
// Some calculation code
return bankOperation.OperationInfo1;
}
private decimal CalculateOperation2(BankOperation bankOperation)
{
// Some calculation code
return bankOperation.OperationInfo2;
}
private decimal CalculateOperation3(ITotalAmount bankOperation)
{
// Some calculation code
return bankOperation.TotalAmount;
}
}
public struct BankAccount : ITotalAmount
{
public decimal TotalAmount { get; set; }
public BankOperation LastOperation { get; set; }
public BankOperation PreviousOperation { get; set; }
}
public interface ITotalAmount
{
decimal TotalAmount { get; set; }
}
public struct BankOperation : ITotalAmount
{
public decimal TotalAmount { get; set; }
public long Rubles { get; set; }
public short Kopeks { get; set; }
public long RublesBeforeOperation { get; set; }
public short KopeksBeforeOperation { get; set; }
public long RublesAfterOperation { get; set; }
public short KopeksAfterOperation { get; set; }
public long OperationInfo0 { get; set; }
public long OperationInfo1 { get; set; }
public long OperationInfo2 { get; set; }
public long OperationInfo3 { get; set; }
public long OperationInfo4 { get; set; }
public long OperationInfo5 { get; set; }
public long OperationInfo6 { get; set; }
public long OperationInfo7 { get; set; }
public long OperationInfo8 { get; set; }
public long OperationInfo9 { get; set; }
public long OperationInfo10 { get; set; }
public long OperationInfo11 { get; set; }
public long OperationInfo12 { get; set; }
public long OperationInfo13 { get; set; }
public long OperationInfo14 { get; set; }
public long OperationInfo15 { get; set; }
public long OperationInfo16 { get; set; }
public long OperationInfo17 { get; set; }
public long OperationInfo18 { get; set; }
public long OperationInfo19 { get; set; }
public long OperationInfo20 { get; set; }
public long OperationInfo21 { get; set; }
public long OperationInfo22 { get; set; }
public long OperationInfo23 { get; set; }
public long OperationInfo24 { get; set; }
public long OperationInfo25 { get; set; }
public long OperationInfo26 { get; set; }
public long OperationInfo27 { get; set; }
public long OperationInfo28 { get; set; }
public long OperationInfo29 { get; set; }
public long OperationInfo30 { get; set; }
public long OperationInfo31 { get; set; }
public long OperationInfo32 { get; set; }
public long OperationInfo33 { get; set; }
public long OperationInfo34 { get; set; }
public long OperationInfo35 { get; set; }
public long OperationInfo36 { get; set; }
public long OperationInfo37 { get; set; }
public long OperationInfo38 { get; set; }
public long OperationInfo39 { get; set; }
public long OperationInfo40 { get; set; }
public long OperationInfo41 { get; set; }
public long OperationInfo42 { get; set; }
public long OperationInfo43 { get; set; }
public long OperationInfo44 { get; set; }
public long OperationInfo45 { get; set; }
public long OperationInfo46 { get; set; }
public long OperationInfo47 { get; set; }
public long OperationInfo48 { get; set; }
public long OperationInfo49 { get; set; }
public long OperationInfo50 { get; set; }
public long OperationInfo51 { get; set; }
public long OperationInfo52 { get; set; }
public long OperationInfo53 { get; set; }
public long OperationInfo54 { get; set; }
public long OperationInfo55 { get; set; }
public long OperationInfo56 { get; set; }
public long OperationInfo57 { get; set; }
public long OperationInfo58 { get; set; }
public long OperationInfo59 { get; set; }
}
Нужно было реализовать метод CalculatePerformed, который делал бы то же самое, но без копирования и боксинга. Вот мое решение:
public decimal CalculatePerformed(in BankAccount bankAccount)
{
return CalculateLastOperationPerformed(in bankAccount) +
CalculatePreviousOperationPerformed(in bankAccount) +
CalculateLastOperationPerformed1(in bankAccount) +
CalculatePreviousOperationPerformed1(in bankAccount) +
CalculateLastOperationPerformed2(in bankAccount) +
CalculatePreviousOperationPerformed2(in bankAccount) +
CalculateLastOperationPerformed3(in bankAccount) +
CalculatePreviousOperationPerformed3(in bankAccount) +
CalculateAccountOperationPerformed3(in bankAccount)
+
CalculateLastOperationPerformed(in bankAccount) +
CalculatePreviousOperationPerformed(in bankAccount) +
CalculateLastOperationPerformed1(in bankAccount) +
CalculatePreviousOperationPerformed1(in bankAccount) +
CalculateLastOperationPerformed2(in bankAccount) +
CalculatePreviousOperationPerformed2(in bankAccount) +
CalculateLastOperationPerformed3(in bankAccount) +
CalculatePreviousOperationPerformed3(in bankAccount) +
CalculateAccountOperationPerformed3(in bankAccount)
+
CalculateLastOperationPerformed(in bankAccount) +
CalculatePreviousOperationPerformed(in bankAccount) +
CalculateLastOperationPerformed1(in bankAccount) +
CalculatePreviousOperationPerformed1(in bankAccount) +
CalculateLastOperationPerformed2(in bankAccount) +
CalculatePreviousOperationPerformed2(in bankAccount) +
CalculateLastOperationPerformed3(in bankAccount) +
CalculatePreviousOperationPerformed3(in bankAccount) +
CalculateAccountOperationPerformed3(in bankAccount);
}
private decimal CalculateLastOperationPerformed(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.LastOperation.OperationInfo0;
}
private decimal CalculatePreviousOperationPerformed(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.PreviousOperation.OperationInfo0;
}
private decimal CalculateLastOperationPerformed1(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.LastOperation.OperationInfo1;
}
private decimal CalculatePreviousOperationPerformed1(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.PreviousOperation.OperationInfo1;
}
private decimal CalculateLastOperationPerformed2(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.LastOperation.OperationInfo2;
}
private decimal CalculatePreviousOperationPerformed2(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.PreviousOperation.OperationInfo2;
}
private decimal CalculateLastOperationPerformed3(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.LastOperation.TotalAmount;
}
private decimal CalculatePreviousOperationPerformed3(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.PreviousOperation.TotalAmount;
}
private decimal CalculateAccountOperationPerformed3(in BankAccount bankAccount)
{
// Some calculation code
return bankAccount.TotalAmount;
}
Как проверить что нет операции боксинга? Только через IL код или еще как нибудь можно? И да, на сколько мое решение хорошее, можно ли было сделать лучше и как? Бенчмарк:
// Результаты
// | Method | Mean | Error | StdDev | Rank | Gen0 | Allocated |
// |------------------- |-----------:|---------:|--------:|-----:|-------:|----------:|
// | CalculatePerformed | 833.5 ns | 7.99 ns | 7.48 ns | 1 | - | - |
// | Calculate | 2,021.7 ns | 10.17 ns | 8.49 ns | 2 | 3.2120 | 6720 B |
Ответы (1 шт):
Боксинг влечёт за собой объект. По определению. А если боксинга нет - ну значит и объекта нет.
Пользуйте множество методов из System.GС и всякие связанные с ним GCMemoryInfo. Это всё можно запустить перед тестом, и после теста. И получить абсолютно все данные по количеству объектов, сколько они занимают памяти и так далее. Если у вас боксинг идёт - так у вас количество объектов будет увеличиваться и GC это покажет, и использование памяти этими объектами тоже покажет, и так далее. А если объектов нет - ну значит и боксинга нет.
И на самом деле это всё нужно смотреть в отладчиках, они в общем то простые и показывают объекты. Вплоть до какого нибудь кучерявого вин-дбг, и даже там можно увидеть хип, и как он растёт (если растёт).
И на самом деле, даже обычный бенч от майкрософта показывает боксинг. Ну т.е. если вы запустили бенч и увидели расход памяти - это и есть боксинг. Которого быть не должно. И если ваш тестируемый метод не пользует память на куче - это и значит что боксинга нету (в вашем случае нету).
Ваше решение (в первом простейшем грубом приближении) тогда хорошее, когда оно исполняется как минимум в два раза быстрее чем было, иначе оптимизации мало чего дают. И второе, может быть более существенное - это ваше решение потребляет сильно меньше памяти на куче, т.е. вы не плодите объекты. Условно говоря у вас был расход на куче 4Кб, стал 32б - вот это хорошо, это замечательно. А совсем хорошо когда строго 0б, это когда вы кучу не используете вообще.
Совсем дикий способ, и он не всегда показывает то что вам нужно. В идеале вы запускаете приложение, и посмотрите на память в банальном диспетчере задач. Вы теребонькаете ваше приложение, и память ДОЛЖНА СТОЯТЬ. Т.е. она вообще не должна меняться. Условно говоря как было например 8 232 Кб - вот так оно и должно быть. Вы пользуете вашу программу, а память стоит. Вообще не меняется. Никак. Это то, к чему вы должны прийти. В идеале.
Вы показали некий код, там структуры весьма длинные. И размер этих структур - тривиально считаем. Впрочем там зачем то short используется, и с выравниванием будут проблемы, но размер посчитать (хотя бы прикинуть размер - всегда можно). Так вот ваши большие структуры на тестах будут отжырать память вёдрами (в случае боксинга), запустите миллион структур - они улетят в хип, и вы увидите что память скачет как бешаная. Это и есть боксинг (в вашем конкретном случае). Это когда вы плодите множество объектов (дахрилион), а коллектор их подчищает за вами. И всё это выглядит циклически, как пила. Пачка объектов - очистка - пачка объектов - очистка. Это можно увидеть даже в диспетчере задач (при условии если много объектов).