问题描述
我正在尝试在一个简单的 .net 核心 Api 中复制享元方法模式,以查看与不使用该模式相比节省了多少内存。
我有两种方法,第一种方法不使用模式创建 5000 个对象,另一种方法使用模式创建 5000 个对象。在他们每个人创建对象后,然后他们调用一个方法来返回应用程序使用的当前内存。
public class MemoryService : IMemoryService
{
private readonly TreeFactory _treeFactory;
public MemoryService()
{
_treeFactory = new TreeFactory();
}
//create without pattern
public long SetobjectsMemory()
{
List<Tree> trees = new List<Tree>();
for (int i = 0; i < 5000; i++)
{
var tree = new Tree()
{
Id = new Random().Next(1,9999999),Part = new PartTree()
{
Name = "Nameany",Bark = "Barkany",Color = "Colorany"
}
};
trees.Add(tree);
};
return Utilities.GetCurrentMemoryUsed();
}
//crete with flyweight pattern
public long SetobjectsMemoryFactory()
{
List<Tree> trees = new List<Tree>();
for (int i = 0; i < 5000; i++)
{
var tree = new Tree()
{
Id = new Random().Next(1,Part = _treeFactory.GetPartTree("Nameany","Barkany","Colorany")
};
trees.Add(tree);
}
return Utilities.GetCurrentMemoryUsed();
}
}
我使用这种模式就像一个使用部件列表的类,如果存在则返回一个部件对象。
public class TreeFactory
{
private static List<PartTree> _parts;
public TreeFactory() {
_parts = new List<PartTree>();
}
public PartTree GetPartTree(string name,string bark,string color)
{
if (_parts.Any(x => x.Name == name && x.Bark == bark && x.Color == color))
{
return _parts.Where(x => x.Name == name && x.Bark == bark && x.Color == color).FirstOrDefault();
}
else {
var newpart = new PartTree()
{
Name = name,Bark = bark,Color = color
};
_parts.Add(newpart);
return newpart;
}
}
}
获取App当前使用内存的方式是使用Process这种方式(在Utilities类中):
public static long GetCurrentMemoryUsed() {
Int64 memory;
using (Process proc = Process.GetCurrentProcess())
{
memory = proc.PrivateMemorySize64 / (1024 * 1024);
}
return memory;
}
在我的 Startup 中,我像单例一样注入 MemoryService。在控制器中,我使用 3 种方法来调用函数:
[HttpGet,Route(nameof(WeatherForecastController.GenerateMemory))]
public IActionResult GenerateMemory()
{
var total=_memoryService.SetobjectsMemory();
return Ok(total);
}
[HttpGet,Route(nameof(WeatherForecastController.GenerateLiftMemory))]
public IActionResult GenerateLiftMemory()
{
var total = _memoryService.SetobjectsMemoryFactory();
return Ok(total);
}
[HttpGet,Route(nameof(WeatherForecastController.GetMemory))]
public IActionResult GetMemory()
{
var total = Utilities.GetCurrentMemoryUsed();
return Ok(total);
}
问题是:当我在导航器中调用控制器中的方法无模式(/weatherforecast/GenerateMemory),然后返回(current)+2mb,但是当我调用方法时 使用模式 (/weatherforecast/GenerateLiftMemory) 返回(current)+3mb。
为什么有模式享元的方法比没有模式的方法返回更多使用的MB(增长)??
带有用于测试的代码的存储库。 Gitlab repository memory api
解决方法
使用 TreeFactory
的代码消耗更多内存,因为它的 GetPartTree
方法在循环中被多次调用,因此其中的 Linq
方法 Any
和 Where
.这两种方法都会在后台创建额外的 Iterator
对象,以便遍历集合并导致额外的内存消耗。
我使用 BenchmarkDotNet 编写了简单的基准测试,并提供了更多选项来演示该问题
扩展内存服务
public class MemoryService : IMemoryService
{
private const int TreeCount = 50000;
private readonly TreeFactory _treeFactory;
public MemoryService()
{
_treeFactory = new TreeFactory();
}
//crea objetos en memoria sin patrones
public decimal SetObjectsMemory()
{
List<Tree> trees = new List<Tree>();
for (int i = 0; i < TreeCount; i++)
{
var tree = new Tree()
{
Id = 1,Part = new PartTree()
{
Name = "Nameany",Bark = "Barkany",Color = "Colorany"
}
};
trees.Add(tree);
};
return Utilities.GetCurrentMemoryUsed();
}
//crea objetos en memoria usando patron flyweight
public decimal SetObjectsMemoryFactory()
{
List<Tree> trees = new List<Tree>();
for (int i = 0; i < TreeCount; i++)
{
var tree = new Tree()
{
Id = 1,Part = _treeFactory.GetPartTree("Nameany","Barkany","Colorany")
};
trees.Add(tree);
}
return Utilities.GetCurrentMemoryUsed();
}
public decimal SetObjectsMemoryFactoryImproved()
{
List<Tree> trees = new List<Tree>();
for (int i = 0; i < TreeCount; i++)
{
var tree = new Tree()
{
Id = 1,Part = _treeFactory.GetPartTreeImproved("Nameany","Colorany")
};
trees.Add(tree);
}
return Utilities.GetCurrentMemoryUsed();
}
//crea objetos en memoria usando patron flyweight
public decimal SetObjectsMemoryFactoryWithoutLambda()
{
List<Tree> trees = new List<Tree>();
for (int i = 0; i < TreeCount; i++)
{
var tree = new Tree()
{
Id = 1,Part = _treeFactory.GetPartTreeWithoutLambda("Nameany","Colorany")
};
trees.Add(tree);
}
return Utilities.GetCurrentMemoryUsed();
}
}
扩展树工厂
public class TreeFactory
{
private static List<PartTree> _parts;
public TreeFactory()
{
_parts = new List<PartTree>();
}
public PartTree GetPartTree(string name,string bark,string color)
{
if (_parts.Any(x => x.Name == name && x.Bark == bark && x.Color == color))
{
return _parts.Where(x => x.Name == name && x.Bark == bark && x.Color == color).FirstOrDefault();
}
var newpart = new PartTree()
{
Name = name,Bark = bark,Color = color
};
_parts.Add(newpart);
return newpart;
}
public PartTree GetPartTreeImproved(string name,string color)
{
var existingPart = _parts.Where(x => x.Name == name && x.Bark == bark && x.Color == color).FirstOrDefault();
if (existingPart != null)
return existingPart;
var newpart = new PartTree()
{
Name = name,Color = color
};
_parts.Add(newpart);
return newpart;
}
public PartTree GetPartTreeWithoutLambda(string name,string color)
{
for (int i = 0; i < _parts.Count; i++)
{
var x = _parts[i];
if (x.Name == name && x.Bark == bark && x.Color == color)
return x;
}
var newpart = new PartTree()
{
Name = name,Color = color
};
_parts.Add(newpart);
return newpart;
}
}
在单独的控制台项目中进行基准测试
class Program
{
static void Main(string[] args)
{
var result = BenchmarkRunner.Run<MemoryBenchmark>();
}
}
[MemoryDiagnoser]
public class MemoryBenchmark
{
private IMemoryService memoryService;
[GlobalSetup]
public void Setup()
{
memoryService = new MemoryService();
}
[Benchmark]
public object SimpleTrees()
{
var trees = memoryService.SetObjectsMemory();
return trees;
}
[Benchmark]
public object FlyTrees()
{
var trees = memoryService.SetObjectsMemoryFactory();
return trees;
}
[Benchmark]
public object FlyTreesImproved()
{
var trees = memoryService.SetObjectsMemoryFactoryImproved();
return trees;
}
[Benchmark]
public object FlyTreesWithoutLambda()
{
var trees = memoryService.SetObjectsMemoryFactoryWithoutLambda();
return trees;
}
}
及其结果
方法 | 平均 | 错误 | StdDev | Gen 0 | Gen 1 | 第 2 代 | 已分配 |
---|---|---|---|---|---|---|---|
简单树 | 9.040 毫秒 | 0.1804 毫秒 | 0.2346 毫秒 | 718.7500 | 453.1250 | 265.6250 | 4.44 MB |
飞树 | 19.701 毫秒 | 0.1716 毫秒 | 0.1521 毫秒 | 2500.0000 | 906.2500 | 437.5000 | 15.88 MB |
FlyTreesImproved | 18.075 毫秒 | 0.2869 毫秒 | 0.2684 毫秒 | 1781.2500 | 625.0000 | 312.5000 | 10.92 MB |
FlyTreesWithoutLambda | 4.919 毫秒 | 0.0273 毫秒 | 0.0242 毫秒 | 421.8750 | 281.2500 | 281.2500 | 2.53 MB |