当然,较少的有效负载等于较少的对象数量,但请阅读下面的完整描述。
在现代浏览器上扩展javascript项目时,哪一个更重要?数据有效负载的大小或内存中javascript对象的数量。我有一个巨大的JSON字符串,我正在循环它,并将它切成不同的对象。JSON字符串包含大量的旅行者信息,并且每个Javascript对象都有很多属性。当JSON中有超过10,000个旅行者时,浏览器很难执行。
我带来了许多不必要的属性,如果我可以减少属性的数量,显然我的有效负载会减少,但对象的数量可能保持不变。
多个JS对象和较小的有效负载,哪一个在性能方面更划算?
谢谢
发布于 2017-09-15 07:44:21
我喜欢我读过的一些答案,但我想我会用一种不同的方式来处理瓶颈问题,我希望你也能避免未来的瓶颈。
大多数答案都假设对象的数量是瓶颈。我认为情况并非如此。我认为瓶颈是JavaScript事件循环过度积压的事实。
如您所知,JavaScript只运行一个线程和一个事件循环。
您调用的每个函数实际上都是此类事件的回调。
但是,因为只有一个线程,所以网页中的任何代码都有来等待每个事件完成,然后才能执行任何其他任务。
这意味着对于JavaScript函数来说,分段(分成微事件/回调)比任何单个函数成为面向性能的更重要。
在您的例子中,您既要遍历一个长字符串,又要执行操作-,而不是将控制返回给事件循环-这意味着浏览器必须等待这一大块代码完成,才能处理更多的数据/事件。
数据收集/处理的问题可以争论。接收大量小消息(可能会增加网络/服务器的负载)是不是更好?接收一个巨大的字符串并按块处理它是不是更好?
..。我不知道,这真的取决于其他因素,例如服务器的设计,数据库的设计,客户端负载,更新间隔等。
如果您确实更喜欢处理单个巨大的字符串,那么一次处理它一点,然后将其转发到回调以供将来处理可能会更好。
例如,对于\n分离的JSON字符串,您可以尝试:
function consumeString(s) {
if(s.length == 0)
return;
var sep = s.indexOf("\n");
if(sep < 0)
sep = s.length;
try {
var obj = JSON.parse(s.slice(0, sep));
console.log("processed:", obj);
} catch {
console.log("Failed... not valid JSON?:");
}
// schedule the next slice for later processing.
setTimeout(consumeString, 0, s.slice(sep + 1));
}
var text = '{ "employees" : [' + // JSON1
'{ "firstName":"John 1" , "lastName":"Doe 1" },' +
'{ "firstName":"Anna 1" , "lastName":"Smith 1" },' +
'{ "firstName":"Peter 1" , "lastName":"Jones 1" } ]}' + // END JSON1
"\n" +
'{ "employees" : [' + // JSON2
'{ "firstName":"John 2" , "lastName":"Doe 2" },' +
'{ "firstName":"Anna 2" , "lastName":"Smith 2" },' +
'{ "firstName":"Peter 2" , "lastName":"Jones 2" } ]}';
consumeString(text);显然,这只是一个概要,但尽管它看起来性能较差(它浪费时间重新调度自己,并且不断中断,增加了CPU缓存未命中的机会)……从用户的角度来看,它实际上帮助浏览器保持响应性,并提高了感知到的性能。
发布于 2017-09-15 09:07:27
对于现代JavaScript VM来说,10,000个对象似乎并不多。您的问题似乎源于您正在内存中处理大量字符串和更新DOM,这两者都是缓慢且占用大量内存的操作。
注意Strings are immutable in JavaScript,所以每次你切它的时候,你都是在内存中创建它的新实例。如果您在循环中进行字符串操作,这一点尤其糟糕。每次迭代都会导致在内存中创建一个新的String对象。旧的现在被丢弃,但可能不会立即得到垃圾收集,因此将消耗内存。如果你正在操作大量的字符串,问题会变得更糟。对于这种长时间在内存中运行的操作,Javascript并不是很好用,因为它是single threaded。这样的操作占用了主线程,这会使您的应用程序变慢。
while(...) {
// this will create a new instance of fullStr in memory every iteration
fullStr = fullStr + 'str1';
}如果操作不正确,即使使用像React这样的现代JS库,DOM操作也可能非常慢。如果您一直通过将新节点附加到现有DOM来呈现新行,您将会看到性能问题。DOM操作导致回流和重绘,这两者都是非常slow和计算密集型的。每次调用render都可能导致完全的回流和重绘。如果你的高阶组件在属性改变时被渲染,它可能会强制所有子组件重新渲染,这会减慢你的页面。React确实通过批量更新解决了其中的一些问题。
很多人没有考虑的另一件事是对mousemove或scroll等事件使用事件处理程序的副作用。每一个微小的移动都可能导致页面上的元素以某种方式发生变化,这也可能导致DOM的其他部分重新绘制或重排。当在页面上使用大量动画时,情况可能会变得更糟。这些类型的事件处理程序还可以通过不断触发来占用VM事件循环。解决这个问题的一种方法是使用throttling。
最不可能,但需要考虑的是,对象在创建时,会有额外的引用计数开销。对我们来说,它可能看起来像是VM将一个对象及其所有属性和值存储在一个连续的内存块中。然而,在reality中,对象存储对存储在内存中其他位置的实际值的引用。对于大量的对象,这种开销不是微不足道的。
有一种误解认为,创建class或function的新实例会导致大量内存开销,因为所有属性和值都是克隆的。这是真的,而不是真的。 is true。通过与特定类或函数的所有实例共享相同的属性,JavaScript使用prototypes可以减少内存使用量。这就是为什么原型继承在内存使用方面非常有效的原因。但是,深原型链可能会导致查找速度很慢。
发布于 2017-09-09 21:17:11
我将尽我所能给出最好的答案,考虑到我的专业领域只跨越了这个问题的范围,而不是完全涵盖了这个问题。
现代计算机有一个存储器层次结构:主存储器;两级或三级高速缓存。每一级高速缓存都比它上面的一级更小,但更接近CPU (实际上也更快)。
因此,就程序运行的速度而言,它使用了多少总内存往往比当前的“工作集”-程序在某个时刻使用的内存部分-不那么重要。
从您的描述来看,将大的JSON字符串拆分成许多较小的对象在大多数情况下将有助于减小工作集的大小。如果在这样做的过程中,您留下了许多未使用的属性,这可能无关紧要,因为它们可能大部分时间都会留在工作集之外。
让后端过滤掉一些不需要的属性可能是一种有用的技术,因为这将减少传输的数据量。但是,您需要仔细考虑如何扩展;它可能会给服务器带来过多的负载。
可能有一些方法可以通过重新评估您编写的算法和您使用的库(方法)来加速您的程序。可能有一些特定于典型现代JavaScript引擎的技术;恐怕这超出了我的专业范围。
在您的应用程序中引入一些新概念可能会有所帮助。通过采用启发式(系统猜测)或降低准确性来减少处理是否可以接受?
例如,可以基于仅使用部分数据的第一次传递向用户呈现临时结果。旋转器(或消息)可以用来提醒用户正在进行更多的处理。处理完完整的数据集后,将其呈现给用户。
或者,让用户选择“快速”或“深度”结果;可能会在后端计算深度结果(这可能会扩展到HTTP或数据库服务器以外的机器)。
祝你好运!
https://stackoverflow.com/questions/45822699
复制相似问题