-
Notifications
You must be signed in to change notification settings - Fork 3.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
Memory pressure... #71
Comments
The static versions are useful because user's don't need to allocate, for example, |
I understand the reasoning, and we can certainly have static versions of every method too if we think it's worth while. In my experience so far, however, I haven't needed the static versions of anything and it just seems like a lot of redundant code to have both. I think the static versions it the type of thing that sounds really useful, but in practice doesn't really add a lot of value (but adds a lot of code). I could be convinced otherwise though. |
Another thought. If we're smart, we would implement the prototype versions in terms of the static versions to avoid duplicate code. However, this adds an extra method call to the prototype versions; which unless the compiler is doing some inlining would certainly make the prototype versions slower. Since the static versions would be faster, it would make sense to use them everywhere at that point, and then get rid of the prototype versions. Of course this is some conjecture on my part; if the compilers are fast enough to eliminate that extra method call, this point is moot. |
For Cesium users, they are very useful. I wanted them several times when dog-fooding. In fact, we decided that all public interfaces should take I'm not sure about the inlining. I also argued against static functions because it is double the amount of code to write, document, test, etc., but it is only for a handful of classes, so I suppose we can justify it. @shunter and I have debated this several times over the past six months or so. |
Yeah, I've talked to @shunter a lot about it too. As I said, I have no problem having both, so here's my proposal.
Assuming we all agree on this, it's something I want to work on right after the viewer pull request is in. |
Supporting (2) sounds a bit painful, right? Sometimes there will be: if (result) { if (result === this) { // ... } else { // ... } } else { // ... } Maybe that case is rare enough. Have you looked at other popular JavaScript vector and matrix libraries? If after looking at them, you still propose the above, I am OK with it. |
The logic can be simplified a lot. At the start:
Then, make all of the internal work within the methods functional instead of object-oriented, that is, use the static versions internally. |
I haven't looked at the other libraries, but I'll check them out. I've already implemented my strategy in a few classes and it's not hard at all and works really well. For a simple example, see the new Spherical class in the viewer branch: https://github.com/AnalyticalGraphicsInc/cesium/blob/viewer/Source/Core/Spherical.js For something more complicated, I've also made some change to Quaternion, check out Quaternion.prototype.multiply. (there's no static version yet) https://github.com/AnalyticalGraphicsInc/cesium/blob/viewer/Source/Core/Quaternion.js I don't plan on doing any more modifications to the viewer branch, as my goal was to do the minimum needed for the branch. I'll do everything else on a new branch. |
@shunter Does that work all the time? The following fails when Cartesian3.prototype.cross = function(other, result) { if (typeof result === 'undefined') { result = new Cartesian3(); } Cartesian3.cross(this, other, result); return result; }; Cartesian3.cross = function(left, right, result) { result.x = left.y * right.z - left.z * right.y; result.y = left.z * right.x - left.x * right.z; result.z = left.x * right.y - left.y * right.x; }; |
Your implementation of Cartesian3.cross is bad, it should do the property look ups first, this one works in your example. This is also probably a little faster (though negligibly so), since it eliminates multiple property look ups. Cartesian3.cross = function(left, right, result) { var leftX = left.x var leftY = left.y var leftZ = left.z var rightX = right.x var rightY = right.y var rightZ = right.z result.x = leftY * rightZ - leftZ * rightY; result.y = leftZ * rightX - leftX * rightZ; result.z = leftX * rightY - leftY * rightX; }; |
Actually, my |
Cartesian3.prototype.cross = function(other, result) {
return cross(this, other, result);
};
var cross = Cartesian3.cross = function(left, right, result) {
if (typeof result === 'undefined') {
result = new Cartesian3();
}
var leftX = left.x;
var leftY = left.y;
var leftZ = left.z;
var rightX = right.x;
var rightY = right.y;
var rightZ = right.z;
result.x = leftY * rightZ - leftZ * rightY;
result.y = leftZ * rightX - leftX * rightZ;
result.z = leftX * rightY - leftY * rightX;
return result;
}; |
And by "bad" I meant incorrect for its purpose, I didn't mean to call your code "bad". |
Looks like me and @shunter implemented the same exact functions independently of each other. I'm going to name this "proof by duplication". |
@mramato I based mine on yours, just to get it as concise I can. Sorry to invalidate your proof. |
Long story short, yes, I'm in full support of this, but, as I originally stated, you have to agree it is quite a bit more than our current code: Cartesian3.prototype.cross = function(other) { return new Cartesian3( this.y * other.z - this.z * other.y, this.z * other.x - this.x * other.z, this.x * other.y - this.y * other.x); }; |
Might be worth looking at these too - http://stepheneb.github.com/webgl-matrix-benchmarks/matrix_benchmark.html |
Awesome, thanks. I had previously checked out some of these library and all of the fast ones followed patterns similar to what I laid out above, so I think we're on the right track. Looks like this site is available on github here: https://github.com/stepheneb/webgl-matrix-benchmarks It would be great for someone to add Cesium to the mix, this way we know how were stand (and what's possible). |
1. All prototype functions now have static equivalents. 2. All functions now do proper error checking. 3. All functions no longer require Cartesian3 instances and instead will work with any object that has an x, y, and z properties. 4. All functions that produce a Cartesian3 now take an optional result parameter. 5. Code coverage for Cartesian3 is now 100% 6. Remove a few unused functions. 7. Add several functions to Cartesian2 that already existed on Cartesian3 (everything but cross). These changes are related to issues #71, #84, #86 (those issues will remain open until we address them for every Core type).
1. All prototype functions now have static equivalents. 2. All functions now do proper error checking. 3. All functions no longer require Cartesian4 instances and instead will work with any object that has an x, y, z, and w properties. 4. All functions that produce a Cartesian4 now take an optional result parameter. 5. Code coverage for Cartesian4 is now 100% 6. Remove Math.angleBetween because it already exists on the Cartesian functions. These changes are related to issues #71, #84, #86 (those issues will remain open until we address them for every Core type).
I'm closing this issue. While memory pressure is still a problem in some areas of the code-base, as a team we are much more aware of them and fix them as we come across them. Core itself is in pretty good shape. There's no need to have an overly general issue like this. |
Almost all of our types have methods that create new instances, which leads to a ton of unnecessary throw away objects and increases memory pressure. For example, computing the sun position ends up creating several Cartesians every frame, when in reality it shouldn't need to create any (or at most 1). All of our functions that create new instances should allow for a result parameter to be specified as the last parameter which is the instance to store the result in. It should also allow for the result parameter to be the same as "this" so that you can do in-place modification.
For example:
Cartesian3.prototype.normalize()
becomes
Cartesian3.prototype.normalize(result)
If result is undefined, then we create a new instance. In either case, we return the result for easy chaining.
We should then revisit all of our existing code and make sure we are using private scratch objects and passing result parameters to eliminate all throw away objects.
I know we originally thought we might want static versions of all of our methods as well, but if we follow the above strategy, that won't be necessary. I've done some of this work on the viewer branch (the minimum necessary to make it performant) but I (or someone else) should do a full refactor everywhere after that branch comes in.
The text was updated successfully, but these errors were encountered: