好的,有几种解决方案 - 但是几乎所有的解决方案似乎都因为某些限制而被排除。
我们发现无论有多少个线程分配内存,应用程序每秒只能执行N次内存分配。
因此,我们可以取消任何增加线程数量的想法(因为“无论有多少个线程”...)。
假设我们无法更改正在分配的内存块的大小。
显然,我们必须分配相同大小的块。
假设我们无法减少动态分配的内存使用。
我觉得这个最有趣了...让我想起了一个关于FORTRAN程序员的故事(在Fortran没有动态内存分配之前),他只是用一个在堆栈上分配的巨大静态数组作为私有堆。不幸的是,这个限制阻止了我们使用这样的技巧.. 然而,它确实为解决方案的一个方面提供了一些线索。
在执行开始时(无论是程序还是每个线程),进行多次^内存分配系统调用。然后在程序中稍后使用这些内存(以及现有的动态内存分配)。
* 注意:“几次”可能是一个精确的数量,根据您在问题开头提到的“分析”来确定。
诀窍 就是修改内存分配的时间。
看起来是一个具有挑战性的问题,尽管没有细节,你只能猜测一些。(这很可能是这个问题的目的)
这里的限制是分配数量,而不是分配大小。 如果我们可以假设你控制着分配位置,你可以一次性为多个实例分配内存。请将下面的代码视为伪代码,因为它仅用于说明目的。
const static size_t NR_COMBINED_ALLOCATIONS = 16;
auto memoryBuffer = malloc(size_of(MyClass)*NR_COMBINED_ALLOCATIONS);
size_t nextIndex = 0;
// Some looping code
auto myNewClass = new(memoryBuffer[nextIndex++]) MyClass;
// Some code
myNewClass->~MyClass();
free(memoryBuffer);
你的代码很可能会变得更加复杂,但你很可能会克服这个瓶颈。如果你必须返回这个新的类,你甚至需要更多的代码来进行内存管理。
根据这些信息,你可以编写自己的 STL 的分配器实现,覆盖 'new' 和 'delete' 操作符......
如果这还不够,尝试挑战限制。为什么你只能进行固定数量的分配,是因为唯一锁定吗?如果是这样,我们能不能改进呢?为什么你需要那么多的分配,改变正在使用的算法是否会解决这个问题......
因为它们共享一个池,所以每个线程必须通过某些关键部分阻塞机制(可能是信号量)获得访问权限。
更多竞争动态内存(即使用new)的线程将导致更多的关键部分阻塞。
任务之间的上下文切换是时间浪费的主要原因。
如何提高速率?
选项1 - 序列化使用...这意味着,您不能简单地尝试在另一个级别使用信号量。我曾经工作过的一个系统,在系统启动期间发生了高动态内存利用。在这种情况下,最容易的方法是更改启动方式,使得此集合的线程n+1仅在线程n完成其初始化并进入其等待输入循环后才开始运行。只有1个线程在进行启动时(而且几乎没有其他动态内存用户正在运行),不会发生关键部分阻塞。4个同时启动需要30秒。4个序列化启动在5秒钟内完成。
选项2 - 为每个特定线程提供一组RAM和私有的new/delete。如果只有一个线程同时访问池,则不需要关键部分或信号量。在嵌入式系统中,挑战在于为线程分配合理的私有池,并避免浪费太多。在具有多GB RAM的桌面上,这可能不是问题。
我认为你可以使用一个单独的线程来负责内存分配。这个线程将拥有一个队列,其中包含线程标识符和所需内存分配的映射。线程不会直接分配内存,而是向队列发送分配请求并进入等待状态。队列会尝试从队列中处理每个请求的内存分配,并唤醒相应的休眠线程。当负责内存处理的线程由于限制无法处理分配时,它应该等待直到可以再次分配内存。
可以像@Tersosauros的解决方案建议的那样构建另一层解决方案来略微优化速度,但它仍应基于上述思想。