这纯粹是为了实验目的和/或学习练习。本质上,我想看看是否可以通过创建一个只初始化一次的类来减少使用
Task.Run(()=>Func<>())
时创建的闭包的占用空间。第一,目标是避免每次运行时创建一个“新”实例,这可能比我想象的闭包本身效率低(但这只是猜测,我知道)。因此,创建一个基本类来执行此操作相当简单,因为您可以在堆栈上找到相关示例。
但是,我遇到的问题是,在我看来,如果我想使用另一个类中的成员和函数,则必须封装它们,或者将它们注入到我们要使用的类中
Run
继续,虽然它可能比原始类本身的数据少,但它可能不会有太大的改进。
所以说,我有一些类似的东西:
internal async Task<PathObject> PopulatePathObjectAsync(Vector3Int origin, Vector3Int destination, PathObject path)
{
return await Task.Factory.StartNew(() => PopulatePathObject(origin, destination, path));
}
/// Not sure if we want to make this a task or not because we may just parallelize and await the outer task.
/// We'll have to decide when we get down to finalization of the architecture and how it's used.
internal PathObject PopulatePathObject(Vector3Int origin, Vector3Int destination, PathObject path)
{
Debug.Log($"Pathfinding Search On Thread: ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
if (!TryVerifyPath(origin, destination, ref path, out PathingNode currentNode))
return path;
var openNodes = m_OpenNodeHeap;
m_ClosedNodes.Clear();
openNodes.ClearAndReset();
openNodes.AddNode(currentNode);
for (int i = CollectionBufferSize; openNodes.Count > 0 && i >= 0; i--)
{
currentNode = ProcessNextOpenNode(openNodes);
if (NodePositionMatchesVector(currentNode, destination))
{
return path.PopulatePathBufferFromOriginToDestination(currentNode, origin, PathState.CompletePath);
}
ProcessNeighboringNodes(currentNode, destination);
}
return path.PopulatePathBufferFromOriginToDestination(currentNode, origin, PathState.IncompletePath);
}
为了放弃 lambda、闭包和委托的创建(或者可能强制转换?),我需要一个实际上完整封装该
PopulatePathObject
函数的类,或者通过逐字复制必要的成员,或者将它们作为参数传递。这一切似乎都可能带来任何好处。那么有什么办法我可以拥有类似的东西..
private class PopulatePathObjectTask
{
private readonly Vector2Int m_Origin;
private readonly Vector3Int m_Destination;
private readonly PathObject m_Path;
public PopulatePathObjectTask(Vector2Int origin, Vector3Int destination, PathObject path)
{
m_Origin = origin;
m_Destination = destination;
m_Path = path;
}
public PathObject PopulatePathObject(Vector3Int origin, Vector3Int destination, PathObject path)
{
///Obviously here, without access to the actual AStar class responsible for the search,
///I don't have access to the functions or the class members such as the heap or the hashset
///that represents the closed nodes as well as the calculated buffer size based on the space-state
///dimensions. With that, I'd just be recreating the class and not avoiding much, if any,
///of the overhead created by the closure capturing the class in the first place.
}
}
我可以用来访问已经存在的功能吗?我一直在考虑创建静态成员并对开放/封闭节点集合使用依赖注入的想法,但我认为,或者更确切地说希望,有人可能对此有更多的了解,除了它毫无意义,甚至可能的开销减少或性能提升将非常小,以至于毫无意义。假设你可能是对的,但我这样做是为了练习,我希望能够实际测量差异。我可能甚至不会使用它,甚至可能会放弃 AStar 而选择 JPS,但在继续之前我想知道。我不完全确定,但似乎闭包必须及时捕获整个 AStar 对象,人们希望通过引用来实现这一点。
public class PopulatePathObjectTask
{
public PopulatePathObjectTask() { }
public int Run()
{
return 42;
}
}
...
var obj = new PopulatePathObjectTask();
var result = await Task.Run(obj.Run);
您还可以使用 TaskFactory.StartNew(Func, Object)
public class PopulatePathObjectTask
{
public PopulatePathObjectTask() { }
public static int Run(object obj)
{
var path = obj as PopulatePathObjectTask;
return 42;
}
}
...
Task.Factory.StartNew(PopulatePathObjectTask.Run, new PopulatePathObjectTask());
但是您可能会注意到,两种替代方案都涉及创建对象,这应该与使用原始 lambda () => PopulatePathObject(origin, destination, path)
没有什么不同,主要区别应该是后者为所有捕获的参数创建一个匿名类。因此,我预计任何替代方案之间几乎没有差异。但是这个开销首先很大吗? C# 对象分配
快。这些对象的大小应该很小(32 字节?),如果对象是“短命的”(即在创建后的第一次GC 中收集),GC 惩罚应该是最小的。我猜想任务调度程序或任务池中的开销将使对象分配开销相形见绌。
但是,您可能想要研究某种类型的闭/开集的如果您“真的”需要优化任务创建,您可能需要编写自己的线程代码,因为高度专业化的代码通常可以胜过通用代码。但只有当我确定这是一个重大问题并且具备解决该问题的知识时,我才会这样做。很高兴,您可以概括 JonasH
为了避免任何额外的基于闭包的开销,
StartNew
action
参数被声明为
static
,它具有编译时保证以避免任何闭包使用。
您需要将
args
ValueTuple
,因为
StartNew
的重载使用
Object
作为
state
参数
据我所知,你不能转换为命名元组,这就是为什么实际的方法调用(
local.Item2(local.Item1)
ValueTuple
var local = (ValueTuple<TState, Func<TState, TResult>>)args;
var (localCallback, localState) = (local.Item2, local.Item1);
return localCallback(localState);
这是一个示例用法await ExecuteActionAsync(Console.WriteLine, 42);
var res = await ExecuteFuncAsync(_ => _, 42);
Console.WriteLine(res);
Dotnet 小提琴:
https://dotnetfiddle.net/aHyTMA