В данной статье раскрываются возможные способы замера производительности выполнения кода, написанного на языке C#.
Рассмотрим такой класс, как Stopwatch
из
пространства System.Diagnostics,
а также способы его использования. Иногда при разработке ПО приходится писать
одновременно несколько идентичных по действию, но различных по реализации функций. Задача состоит в выборе для последующей разработки функции, которая будет выполнятся
быстрее остальных. В
таком случае на помощь придёт класс Stopwatch. Данный класс
позволяет замерить время выполнения конкретного участка кода. Для примера
рассмотрим разные способы чтения с архива с помощью библиотеки SharpCompress. Пример
основан на проблеме с распаковкой файлов с 7zip
архива,
с которым мы столкнулись в реальном проекте.
class Program
{
static void Main(string[] args)
{
var st = new Stopwatch();
st.Start();
TestExtractArchiveFactory();
st.Stop();
var resultArchiveFactory =
st.Elapsed.TotalSeconds;
st.Restart();
TestExtractZipArchive();
st.Stop();
var resultSevenZipArchive =
st.Elapsed.TotalSeconds;
Console.WriteLine("Использование ArchiveFactory {0} сек", resultArchiveFactory);
Console.WriteLine("Использование SevenZipArchive {0} сек", resultSevenZipArchive);
Console.ReadLine();
}
public static void TestExtractZipArchive()
{
try
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "IocContainersDemo.7z");
var extractDirectory =
CreteTempDirectory();
using (Stream stream = File.OpenRead(path))
using (var archive = SevenZipArchive.Open(stream))
{
var reader =
archive.ExtractAllEntries();
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
reader.WriteEntryToDirectory(extractDirectory, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
}
}
}
}
finally
{
DeleteTempDirectory();
}
}
public static void TestExtractArchiveFactory()
{
try
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "IocContainersDemo.7z");
var extractDirectory =
CreteTempDirectory();
using (Stream stream = File.OpenRead(path))
using (var archive = ArchiveFactory.Open(stream))
{
using (var reader =
archive.ExtractAllEntries())
{
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
reader.WriteEntryToDirectory(extractDirectory, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
}
}
}
}
}
finally
{
DeleteTempDirectory();
}
}
public static string CreteTempDirectory()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "TestArchives");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
return path;
}
public static void DeleteTempDirectory()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "TestArchives");
if (Directory.Exists(path))
Directory.Delete(path, true);
}
}
Основная работа с
классом Stopwatch
основывается
на методах Start()
и Stop().
Для простых примеров такой подход с созданием одного объекта Stopwatch; использование его в одной функции и вызов разных функций из этой одной функции является непрактичным. Поэтому рекомендуется написать класс-обертку, который будет иметь
одну функцию и будет использован для замера времени выполнения кода. Этот класс
может быть очень простым, как, например, приведенный ниже.
public static class StopwatchHelper
{
public static double ProfileMethod(Action action)
{
var st = new Stopwatch();
st.Start();
action();
st.Stop();
return st.Elapsed.TotalSeconds;
}
}
После этого
исходный пример немного изменится.
static void Main(string[] args)
{
var resultArchiveFactory = StopwatchHelper.ProfileMethod(() =>
{
TestExtractArchiveFactory();
});
var resultSevenZipArchive = StopwatchHelper.ProfileMethod(() =>
{
TestExtractZipArchive();
});
Console.WriteLine("Использование ArchiveFactory {0} сек", resultArchiveFactory);
Console.WriteLine("Использование SevenZipArchive {0} сек", resultSevenZipArchive);
Console.ReadLine();
}
Как видите,
код стал намного проще. Такой подход с использованием объекта-обертки над
функциями или кодом является популярным подходом при разработке ПО.
Рассмотрим еще один подход для использования класса Stopwatch, который основывается на Aspect Oriented Programming (AOP) с использованием PostSharp. Данный подход базируется на внедрении дополнительного кода в существующий IL код. Посмотрим, как можно использовать возможности PostSharp вместе с Stopwatch.
Рассмотрим еще один подход для использования класса Stopwatch, который основывается на Aspect Oriented Programming (AOP) с использованием PostSharp. Данный подход базируется на внедрении дополнительного кода в существующий IL код. Посмотрим, как можно использовать возможности PostSharp вместе с Stopwatch.
[Serializable]
[ProfilerAspect(AttributeExclude = true)]
public class ProfilerAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
args.MethodExecutionTag = Stopwatch.StartNew();
}
public override void OnExit(MethodExecutionArgs args)
{
var sw = (Stopwatch)args.MethodExecutionTag;
sw.Stop();
string output = string.Format("{0} Executed in {1} seconds",
args.Method.Name,
sw.Elapsed.TotalSeconds);
Console.WriteLine(output);
}
}
Теперь достаточно
пометить метод, который Вы хотите замерить. с помощью атрибута [ProfilerAspect].
[ProfilerAspect]
public static void TestExtractZipArchive()
{
try
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "IocContainersDemo.7z");
var extractDirectory =
CreteTempDirectory();
using (Stream stream = File.OpenRead(path))
using (var archive = SevenZipArchive.Open(stream))
{
var reader =
archive.ExtractAllEntries();
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
reader.WriteEntryToDirectory(extractDirectory, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
}
}
}
}
finally
{
DeleteTempDirectory();
}
}
Для примера был сделан
вывод в консольное окно. Вы можете переписать метод ProfilerAspect
так,
чтобы вывод происходил в Output
Window,
использовав вместо метода Console.WriteLine метод
Debug.WriteLine.
Итоги
В статье рассмотрен подход для замера скорости выполнения кода с помощью класса Stopwatch. В программировании часто приходится сталкиваться с задачами, предполагающими выбор варианта кода, работающего быстрее. Для этого лучше быть знакомым со способами, которые для этого существуют, а также знать, как их можно применить на
практике.
No comments:
Post a Comment