我们分配并释放了许多内存块。我们使用Memory Heap。但是,堆访问成本很高。
为了更快地分配和释放内存,我们采用了全局Free List。当我们制作多线程程序时,空闲列表受Critical Section保护。但是,临界部分会导致并行性瓶颈。
要删除关键部分,我们为每个线程分配一个空闲列表,即Thread Local Storage。但是,线程T1总是存储块,线程T2总是释放它们,因此线程T2中的“空闲列表”总是在增加,而“空闲列表”没有任何好处。
尽管存在关键部分的瓶颈,我们还是采用了一些不同的方法来再次采用关键部分。我们准备了几个空闲列表以及分配给每个空闲列表的关键部分,因此0〜N-1个空闲列表和0〜N-1个关键部分。我们准备一个atomic-operated整数值,该值将先变为0、1、2,... N-1,然后再变为0、1、2...。对于每个分配和释放,我们得到整数X,然后对其进行变异,访问第X个关键部分,然后访问第X个空闲列表。但是,这比以前的方法(使用线程本地存储)要慢得多。原子操作相当慢,因为有更多线程。
由于以非原子方式突变整数值不会导致损坏,因此我们以非原子方式进行了突变。但是,由于整数值有时会过时,因此有很多机会通过不同的线程访问相同的临界区和空闲列表。尽管这比以前的方法要少得多,但这再次导致了瓶颈。
我们使用带有散列的线程ID代替整数值,范围为(0〜N-1),因此性能得到了提高。
我想应该有更好的方法来做到这一点,但是我找不到确切的方法。有什么想法可以改善我们的作品吗?
答案 0 :(得分:0)
处理堆内存是OS的一项任务。没有什么可以保证您可以比操作系统做得更好/更快的。
但是在某些情况下,您可以有所改进,特别是当您了解操作系统不了解的内存使用情况时。 我在这里写下我未经检验的想法,希望您能从中受益。
假设您有T个线程,它们全部都保留和释放内存。主要目标是速度,因此,我将尽量不使用TLS,临界阻塞,而不是原子操作。
If(重复:如果,如果,如果)应用程序可以适合几个离散大小的内存块(非随机大小,以避免碎片和无用的空洞),然后开始向操作系统询问这些离散块的数量
例如,您有一个n1
个块的数组,每个块的大小为size1
,一个n2
个块的数组,每个块的大小为size2
,一个{{1 }}... 等等。每个数组都是二维的,第二个字段仅存储已用/可用块的标志。如果您的数组很大,那么最好对标志使用专用数组(由于连续使用内存总是更快)。
现在,有人要求一块n3
大小的内存。专用函数(或对象或其他对象)搜索大小大于或等于sB
的块数组,然后通过查看used / free标志选择一个块。在结束此任务之前,将正确的阻止标志设置为“已使用”。
当两个或多个线程要求使用相同大小的块时,该标志可能会损坏。使用TLS可以解决此问题,也可以解决严重的阻塞问题。我认为您可以在开始将搜索标记设置为flags-array时设置一个bool标志,使其他线程可以等到标志更改为止,这仅在block标志更改之后发生。使用伪代码:
sB
释放一个块比较容易,只需将其标记为空闲,就没有线程并发问题。
没有空闲块的交易取决于您(等待,使用更大的块,向OS要求更多,等等)。
请注意,整个内存是在应用启动时从操作系统保留的,这可能是个问题。
如果这个想法使您的应用程序运行更快,请告诉我。我可以肯定地说的是,与使用正常的OS请求相比,使用的内存更大。但是如果您选择“最合适”的尺寸(最常用的尺寸)就不会太多。
可以做一些改进: