JavaScript中的内存-超越泄漏(2019年)

2020-06-22 13:52:54

您创建和访问数据的方式可能会影响应用程序的性能。让我们看看怎么做。

JavaScript是一种非常高级的语言。因此,大多数开发人员不倾向于过多地考虑数据是如何存储在内存中的。在本文中,我们将讨论数据是如何存储在内存中的,它如何影响CPU和内存,以及您在JS中分发和访问数据的方式如何影响性能。

当计算机需要进行一些计算时,处理单元(CPU)需要处理数据。因此,根据手头的任务,它通过总线向内存发送获取数据的请求。

CPU比内存快得多。因此,CPU->;总线->;内存->;总线->;CPU的这个过程“浪费”了计算时间。在查找内存时,CPU处于空闲状态,无法执行任何其他操作。

为了防止空闲时间,系统中添加了高速缓存。我们不会详细介绍缓存或缓存类型,但只要说缓存是CPU的内部存储器就足够了。

当CPU收到要运行的命令时,它首先在高速缓存中搜索数据,如果数据不在那里,它就通过总线发送请求。

总线进而带来所请求的数据加上存储器的临时部分,并将其存储在高速缓存中以供快速参考。

这样,CPU将更少地抱怨内存有多慢,从而减少CPU空闲时间。

可能会出现的问题-特别是当我们处理大量数据时-是一种称为“缓存未命中”的现象。

高速缓存未命中意味着在计算过程中,CPU发现它在高速缓存中没有必要的数据,因此需要通过常规通道请求此数据-您知道,这些通道占用内存。

缓存未命中的图示。正在处理来自数组的数据,但也会请求缓存限制之外的数据进行计算-这会造成“缓存未命中”

问得好。但现在,随着越来越多的数据涌入Node.js服务器,甚至不幸的富客户端,您在迭代大型数据集时有更多机会遇到缓存未命中问题。

本地访问所做的是线性遍历数组,并将x设置为0。

如果我们重复此函数100次(查看设置中的Repeats常量),我们可以测量运行所需的时间:

现在,根据我们在上面所学到的,如果我们在迭代期间处理很远的数据,可能会导致缓存未命中。远处的数据是不在相邻索引中的数据。以下是用于此操作的函数:

这里发生的情况是,在每个迭代中,我们寻址与上一次迭代相隔几行的索引。因此,如果行数为1,000(与我们的示例相同),我们将得到以下迭代:[0,1000,2000,…。,11001,2001,…]。。

非本地迭代几乎慢了4倍。随着数据量的增加,这种差异将会扩大。发生这种情况是因为CPU因缓存未命中而处于空闲状态的时间。

那么你付出的代价是什么呢?这完全取决于您的数据大小。

你可能不这么认为,但是…。在某些情况下,您希望使用一些非线性(例如,1,2,3,4,5)或非偶然性(例如,for(设i=0;i<;n;i+=1000))的逻辑访问数组。

例如,您从服务或DB获取数据,并且需要处理按某些复杂逻辑排序或过滤的数据。这可能会导致访问数据的方式类似于FarAccess函数中显示的内容。

查看上图,我们可以看到存储在内存中的数据(顶部灰条)。下面,我们看到数据从服务器到达时创建的阵列。最后,我们看到包含对存储在内存中不同位置的对象的引用的排序数组。

这种遍历排序数组的方式可能会导致上面示例中看到的多个缓存未命中。

请注意,此示例适用于小数组。缓存未命中与大得多的数据相关。

在一个前端需要流畅的动画,在后端(无服务器或其他)每毫秒的CPU时间都可以收费的世界中,这可能会变得非常关键。在这个世界里,你需要在前端使用流畅的动画,而在后端(无服务器或其他)可以按每毫秒的CPU时间收费。

这个问题有多种解决方案,但是现在您知道了影响性能的原因,您可能可以自己想出一个解决方案。您只需将一起处理的数据更紧密地存储在内存中。

让我们继续我们的示例。假设在我们的应用程序中,最常见的过程是使用FarAccess函数中所示的逻辑检查数据。我们希望优化数据,使其在最常见的for循环中运行得更快。

现在,在DiffArr中,索引[0,1,2,…]中的对象。在原始数组中,现在设置为[0,1000,2000,…。,11001,2001年,…。,21002,2002,…]。。这些数字表示对象的索引。这模拟了对数组进行排序-这是实现数据局部性设计模式的一种方式。

为了容易地测试这一点,我们将稍微更改FarAccess函数以获得自定义数组:

瞧!-我们已经优化了我们的数据,以符合我们需要的更常见的观察方式。

在本文中,我们演示了数据局部性设计模式。从本质上讲,我们展示了以未针对其进行优化的方式访问数据结构可能会降低我们的性能。然后,我们优化数据以适应我们处理数据的方式-我们看到它是如何提高性能的。

数据局部性设计模式过去是游戏开发人员的领域,他们必须处理大量迭代多次的实体。在上面的结果中,我们看到即使在JavaScript这样的高级语言中,数据局部性仍然很重要--而且不仅仅是在游戏中。

如今,随着大量数据在服务器之间传输,甚至推送到浏览器中,曾经是游戏开发人员日常工作的设计模式,应该被应用程序开发人员考虑-无论是服务器端还是客户端。