-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Array.from
instead of Array.prototype.slice.call
#1722
Use Array.from
instead of Array.prototype.slice.call
#1722
Conversation
- Use rest parameters - Use `Array.from`
We can’t use the rest operator because it’s not being polyfilled. We don’t polyfill it because it makes the bundle larger, and .slice works fine |
Sorry ... I'm going to remove https://github.com/video-dev/hls.js/blob/master/src/event-handler.js#L18 |
Array.prototype.slice.call
Array.prototype.slice.call
Array.prototype.slice.call
Array.from
instead of Array.prototype.slice.call
The application code can use But, The library(hls.js) code should not use
I think that this PR require benchmark. |
rest parameters is transpiled by babel in hls.js codebase |
Thanks. I don't know this principle (Rather, I think that |
Ah I guess we do! |
I tried benchmarking by the following code ... (function(){
const UINT32_MAX = Math.pow(2, 32);
function randomArrayLike(length) {
const a = [];
for (let i = 0; i < length; i++) {
a.push(Math.floor(Math.random() * UINT32_MAX));
}
return new Uint32Array(a);
}
const loop = 32768;
const length = 16384;
function benchSliceCall(arraylike) {
for (let i = 0; i < loop; i++) {
Array.prototype.slice.call(arraylike, 0);
}
}
function benchFrom(arraylike) {
for (let i = 0; i < loop; i++) {
Array.from(arraylike);
}
}
let arraylike = randomArrayLike(length);
console.time('slice.call1');
benchSliceCall(arraylike);
console.timeEnd('slice.call1');
console.time('from1');
benchFrom(arraylike);
console.timeEnd('from1');
arraylike = randomArrayLike(length);
console.time('from2');
benchFrom(arraylike);
console.timeEnd('from2');
console.time('slice.call2');
benchSliceCall(arraylike);
console.timeEnd('slice.call2');
})(); The results is the following ...
|
This shows a somewhat difference ... Therefore, I think that the browsers that can use native |
I've modified a bit #1722 (comment). (function() {
const UINT32_MAX = Math.pow(2, 32);
function noopPreventOptimizeVM(arg) {
return function(){
return arg;
};
}
function randomArrayLike(length) {
const a = [];
for (let i = 0; i < length; i++) {
a.push(Math.floor(Math.random() * UINT32_MAX));
}
return new Uint32Array(a);
}
const loop = 32768;
const length = 16384;
function benchSliceCall(arraylike) {
let results = [];
for (let i = 0; i < loop; i++) {
const copy = Array.prototype.slice.call(arraylike, 0); // prevent eliminate
results.push(copy.length);
}
return results;
}
function benchFrom(arraylike) {
let results = [];
for (let i = 0; i < loop; i++) {
const copy = Array.from(arraylike); // prevent eliminate
results.push(copy.length);
}
return results;
}
let arraylike1 = randomArrayLike(length);
const startSlice = Date.now();
const r1 = benchSliceCall(arraylike1);
noopPreventOptimizeVM(r1.length);
print("slice:");
print(Date.now() - startSlice);
let arraylike2 = randomArrayLike(length);
const startFrom = Date.now();
const r2 = benchFrom(arraylike2);
noopPreventOptimizeVM(r2.length);
print("from:");
print(Date.now() - startFrom);
})(); Run the microbench on jsvu + eshost.
loop count: 100 result. JSC does not optimize Array.from#### JavaScriptCore slice: 85 from: 190 When JIT is enabled, However, Micro-benchmarking is Hard. If need to more details, We should collection real performance test like #1528 📝 Notes: We can create a util function like const copyArray = (() => {
if (typeof Array.from === 'function') {
return (arrayLike) => {
return Array.from(arrayLike);
}
} else {
return (arrayLike) => {
return Array.prototype.slice.call(arrayLike);
}
}
})(); |
Seems like |
If
I think so too. |
Two questions for everyone discussing here:
|
@Korilakkuma btw the "easier to read" argument isn't that convincing, since you could alias the whole slice thing anyway (which is what you did). The real thing that is bugging me with this code is, why can't we call |
|
@Korilakkuma In principle I agree with you. Doing the feature-detection once is probably worth the potential performance gain anyway. About my previous comment: I was basically questioning a bit the argumentative overhead vs the actual gain we have here from this change :) However, I appreciate a lot that this proposition of yours has brought up a different issue in my opinion around this part of the code: The issue I see is that it is unclear why we are copying this array in the first place. I think a much greater improvement may be to not have to do a copy at all :) It seems we are doing this because that object is at first initialized as a plain |
There is a question.
It seems to be initialized as https://github.com/video-dev/hls.js/blob/master/src/remux/mp4-generator.js#L329 |
@Korilakkuma We are talking about the That is what we call The line you linked is unrelated to the problem. |
I see. Do you think the following code ? // ...
for (i = 0; i < track.sps.length; i++) {
data = track.sps[i];
len = data.byteLength;
sps.push((len >>> 8) & 0xFF);
sps.push((len & 0xFF));
sps = sps.concat(data); // Unnecessary `Array.prototype.slice.call` or `Array.from`
}
// ... |
Well, yes that's what I am saying. I guess however this code you just posted will break because If I'd want to fix it, I'd try and look wehre the |
I think that https://github.com/video-dev/hls.js/blob/master/src/demux/tsdemuxer.js#L712 (But, I'm not sure ...) |
that is this is really confusing :) I guess it's about finding out what If I am not wrong this would actually be a That explains it now. We are actually converting that to a plain Array, because that's what it is in the mp4-generator. Hm, that's pretty bad :/ MP4-generator should use Uint8Array as well. But at least now we know why :D So seems this is the go-to way to convert Uint8Array to Array. Btw people out there claim one should use Array.from where it is available. I also would assume that is more optimized than the slice "hack". So let's please do this feature detection on module decl :) 👍 |
Btw this is a great example how confusing code can be that lacks types, and how they just make things more clearly fit together. |
Is that the following code ? class MP4 {
static copyAsArray = typeof Array.from === 'function' ? Array.from : Array.prototype.slice.call;
/* ... */
static avc1 (track) {
let sps = [], pps = [], i, data, len;
// assemble the SPSs
for (i = 0; i < track.sps.length; i++) {
data = track.sps[i];
len = data.byteLength;
sps.push((len >>> 8) & 0xFF);
sps.push((len & 0xFF));
sps = sps.concat(MP4.copyAsArray(data)); // SPS
}
// assemble the PPSs
for (i = 0; i < track.pps.length; i++) {
data = track.pps[i];
len = data.byteLength;
pps.push((len >>> 8) & 0xFF);
pps.push((len & 0xFF));
pps = pps.concat(MP4.copyAsArray(data));
}
/* ... */
}
/* ... */
} |
hey. what i meant by "on module declaration" is exactly what is the current diff of this PR :) No need for any changes from my side! I don't think it makes any sense to do this as a static method of the class. But, if you'd like to do it really nicely, I would put this into it's own module in Also, it would be great if you could check wether this kind of Uint8Array to Array conversion is being done elswhere in the code and then re-use the same approach consistently. Finally, I would not call it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
almost there!
@@ -0,0 +1,5 @@ | |||
'use strict'; | |||
|
|||
const MakeArrayFromArrayLike = typeof Array.from === 'function' ? Array.from : Array.prototype.slice.call; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lower case this, since it is not a constructor: makeArrayFromArrayLike
|
||
const MakeArrayFromArrayLike = typeof Array.from === 'function' ? Array.from : Array.prototype.slice.call; | ||
|
||
export default MakeArrayFromArrayLike; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export this named, like export makeArrayFromArrayLike
src/remux/mp4-generator.js
Outdated
@@ -3,6 +3,7 @@ | |||
*/ | |||
|
|||
// import Hex from '../utils/hex'; | |||
import makeArrayFromArrayLike from '../utils/make-array-from-array-like'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import this named: import { makeArrayFromArrayLike } from '../utils/make-array-from-array-like';
:) 👍
I see. So I made the following changes.
|
@@ -0,0 +1,3 @@ | |||
'use strict'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need 'use strict' here, please remove that.
we are compiling all this stuff with babel and bundling it with webpack, these tools take care of this stuff.
This PR will...
Use
Array.from
instead ofArray.prototype.slice.call
.Why is this Pull Request needed?
This PR is to use better function than
Array.prototype.slice.call
.Are there any points in the code the reviewer needs to double check?
Resolves issues:
Checklist