-
Notifications
You must be signed in to change notification settings - Fork 50
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
Slow Compiler Build Time #36
Comments
Hi Kenny, What are you using to compile the app? An IDE, Maven, Ant, command-line? If you are not using an IDE, can you attach the console output. Feel free to obfuscate file names. |
Sorry, I should have added this info originally. I'm compiling using command line in FlashDevelop. I'm also using CreateJS and have a high amount of drawing information in each file. I'm curious if the compiler is slowed by parsing a long string like....AAHAjQgBgDACgFIAKgeQADgKgBgFQgB by evaluating every character. Abbreviated Build Command C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion>echo off C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion>D:\flexjssdk/js/bin/asjsc.bat src\FinalPuppet_PassFailVersion_Main.as -external-library-path+=libs-external* -debug=true -external-library-path+="D:\flexjssdk\js\libs\js.swc" Sample Code - I have many files with many lines like the one below. shape_1.graphics.f("#000000").p("AAHAjQgBgDACgFIAKgeQADgKgBgFQgBgEgHAAQgFAAgFADQgEACgEAGQgDAEgEAKIgIAYQgCAFgDADQgEADgEAAQgEAAgCgDQgBgDACgFIASg1QACgGADgCQADgDAEAAIAEABQABABAAAAQAAAAABABQAAAAAAABQAAAAAAABIAAAFIgBADQAGgHAHgDQAHgDAHAAQAIAAAEADQAFAEABAFQABAEgBAEIgDAKIgMAiQgCAFgDADQgEADgEAAQgEAAgCgDg"); |
Hard to say what is causing the problem. 120 files in 3 minutes is one file every 1.5 seconds? How big are the .as files? If you have the skills to run the compiler in a profiler, I'd be interested in seeing the results. If you can attach a file, we can try to compile it and see where the time is going. Have you disabled scan-on-write virus scanners for the output folder? |
FYI, I have seen inflated build times when running the compiler from within a Java process. I'm seen about double what I get from the command line, and nowhere near as bad as what you are reporting. Can you try compiling from the system command line and not within a Java process? |
I was also hoping to see the tail end of the console output, in order to see if there were lots of warnings, or other output. |
Also note that the output "Compiling file.." only starts after all files have been parsed into the AST. Is there a noticeable pause before the first "Compiling file"? |
FWIW, on my personal Windows machine Intel I5, 2GHz, 4GB Ram, SSD, compile time of the entire framework is almost 8 minutes, but drops by 2 minutes if I shut off Windows Defender and another antivirus checker. On my Mac (Intel I7, 2.5GHz, 16GB RAM, SSD), the compile time of the entire framework is 3 minutes. |
Thanks for taking a look at this. I need to do some experiments with command only. I hadn't thought about FlashDevelop's Java process getting in the way. I didn't include the end of the build process since their wasn't any warning or errors. I'll make sure I have windows defender turned off and go command line only to see what I get. I'll let you know when I have more info. |
Also, make sure you are using a 64-bit Java. It looked like FlashDevelop was 32-bit and I'm not sure it can launch the compiler as 64-bit. |
Okay, so switching to 64bit Java made all the difference. I'm seeing about 84 seconds of compile time via command line or FlashDevelop and with all windows or other antivirus turned off. In my project, I do have 2 very large files. One is 2890 lines of code and the other is 21,496. So, I certainly expect some time for these, but the code is still very simple in these files. They only contain the constructor function and its filled with tons of CreateJS shape creation like below. What I'm doing is converting the HTML Canvas output from Adobe Animate into AS3 classes to avoid writing ugly javascript. The only downside is the potential compile time, so any improvement when compiling these large graphic files is much appreciated as this makes a great pipeline for any person or company needing to transition away from Flash Player. I've included a test FlashDevelop project with 2 files with a compile time of 46 seconds on my machine. Currently, I'm ignoring compiler warnings for having public variables in this project. shape_3572 = new Shape(); |
You should be able to set -warn-public-vars-false to disable those warnings. They'll only become important if you minify your output. I will try to get the profiler to run on these files. What does the Adobe Animate output look like? It might make more sense to treat that output as an External Library and only manage your other logic in ActionScript. |
With Animate, I'm using a JSFL script to convert the resulting javascript from a Canvas project into proper AS3 classes. This way, I can write more ActionScript to interact with the exported graphics and extend the different exported classes for button and switches functionality. If there is an easier way to interact with exported javascript in ActionScript, I would love to know. I'm just not aware of anything. Thanks for the compiler flag and again for taking a look at this. Royale is pretty kickass and I can't wait to see where is goes. |
Any existing JS that has a prototype for new instances can be left untouched and treated as a class/library in Royale. You can create what we refer to as "typedefs" or "externs" that represent the API surface of the JS code. If you think about, the airglobal.swc and/or playerglobal.swc allows you to use the C code in the Flash/AIR runtimes from ActionScript. This is essentially the same thing. So, if your JSFL doesn't actually manipulate the body of any methods, then it might be easier to create typedefs instead. That way, if you re-export from Animate you don't have to run JSFL again. The typedef can remain untouched unless you changed the API surface. Post at least a snippet of the Animate JS and we can discuss how typedefs might help. |
This is actually something I've been wanting to look into. I just haven't seen any documentation on how to create them. This would vastly reduce export and compiling time. Here's a quick example from Animate. (function (cjs, an) {
var p; // shortcut to reference prototypes
var lib={};var ss={};var img={};
lib.ssMetadata = [];
// symbols:
// helper functions:
function mc_symbol_clone() {
var clone = this._cloneProps(new this.constructor(this.mode, this.startPosition, this.loop));
clone.gotoAndStop(this.currentFrame);
clone.paused = this.paused;
clone.framerate = this.framerate;
return clone;
}
function getMCSymbolPrototype(symbol, nominalBounds, frameBounds) {
var prototype = cjs.extend(symbol, cjs.MovieClip);
prototype.clone = mc_symbol_clone;
prototype.nominalBounds = nominalBounds;
prototype.frameBounds = frameBounds;
return prototype;
}
(lib.GreenCircle = function(mode,startPosition,loop) {
this.initialize(mode,startPosition,loop,{});
// Layer_1
this.shape = new cjs.Shape();
this.shape.graphics.f("#00FF00").s().p("AojIkQjjjjAAlBQAAlADjjjQDjjjFAAAQFBAADjDjQDjDjAAFAQAAFBjjDjQjjDjlBAAQlAAAjjjjg");
this.shape.setTransform(77.5,77.5);
this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));
}).prototype = getMCSymbolPrototype(lib.GreenCircle, new cjs.Rectangle(0,0,155,155), null);
// stage content:
(lib.AnimateTest = function(mode,startPosition,loop) {
this.initialize(mode,startPosition,loop,{});
// Layer_1
this.instance = new lib.GreenCircle();
this.instance.parent = this;
this.instance.setTransform(357.5,273.5,1,1,0,0,0,77.5,77.5);
this.timeline.addTween(cjs.Tween.get(this.instance).to({x:352.5,y:94.6},29).wait(1));
// Layer_2
this.shape = new cjs.Shape();
this.shape.graphics.f("#000000").s().p("ArFK8IAA13IWLAAIAAV3g");
this.shape.setTransform(121,107.1);
this.timeline.addTween(cjs.Tween.get(this.shape).wait(30));
}).prototype = p = new cjs.MovieClip();
p.nominalBounds = new cjs.Rectangle(325.1,237.1,385,314);
// library properties:
lib.properties = {
id: '75B8C1BBBE52AB4F9D57C9D574755C2B',
width: 550,
height: 400,
fps: 24,
color: "#FFFFFF",
opacity: 1.00,
manifest: [],
preloads: []
};
// bootstrap callback support:
(lib.Stage = function(canvas) {
createjs.Stage.call(this, canvas);
}).prototype = p = new createjs.Stage();
p.setAutoPlay = function(autoPlay) {
this.tickEnabled = autoPlay;
}
p.play = function() { this.tickEnabled = true; this.getChildAt(0).gotoAndPlay(this.getTimelinePosition()) }
p.stop = function(ms) { if(ms) this.seek(ms); this.tickEnabled = false; }
p.seek = function(ms) { this.tickEnabled = true; this.getChildAt(0).gotoAndStop(lib.properties.fps * ms / 1000); }
p.getDuration = function() { return this.getChildAt(0).totalFrames / lib.properties.fps * 1000; }
p.getTimelinePosition = function() { return this.getChildAt(0).currentFrame / lib.properties.fps * 1000; }
an.bootcompsLoaded = an.bootcompsLoaded || [];
if(!an.bootstrapListeners) {
an.bootstrapListeners=[];
}
an.bootstrapCallback=function(fnCallback) {
an.bootstrapListeners.push(fnCallback);
if(an.bootcompsLoaded.length > 0) {
for(var i=0; i<an.bootcompsLoaded.length; ++i) {
fnCallback(an.bootcompsLoaded[i]);
}
}
};
an.compositions = an.compositions || {};
an.compositions['75B8C1BBBE52AB4F9D57C9D574755C2B'] = {
getStage: function() { return exportRoot.getStage(); },
getLibrary: function() { return lib; },
getSpriteSheet: function() { return ss; },
getImages: function() { return img; }
};
an.compositionLoaded = function(id) {
an.bootcompsLoaded.push(id);
for(var j=0; j<an.bootstrapListeners.length; j++) {
an.bootstrapListeners[j](id);
}
}
an.getComposition = function(id) {
return an.compositions[id];
}
})(createjs = createjs||{}, AdobeAn = AdobeAn||{});
var createjs, AdobeAn; |
Kenny, I was able to quickly put together an externs version of the JSFL library. |
Wow, that stuff is hard to read. However it does appear to be using prototypes. What did you end up generating as the ActionScript for, say GreenCircle? To create a typedef in ActionScript, you can just create the methods and properties and leave the method bodies empty or just return a null. I wrote up a blurb on it for FlexJS here. https://github.com/apache/royale-tourjs/blob/develop/cordova/BatchScanner/BatchScanner/src/main/resources/top.html |
@bigosmallm I've actually used that same tool that Josh Tynjala made to create a swc for CreateJS and @aharui I've created AS3 stubs to create swc files for other javascript libraries before too, but never added typedef to javascript. With the Animate output, I still need to extend the resulting classes. Basically, I'm trying to keep the original workflow using Animate where you can extend classes and write some basic timeline code. This is all to minimize the impact on workflow for the company I'm at as we transition to HTML5. Also, is there a way to include the external javascript in the build process, so closure will combine it with the minified version? Here's the resulting ActionScript for the Circle. package com.AnimateTest
{
import createjs.*;
public class GreenCircle extends MovieClip
{
public var shape:Shape;
public function GreenCircle(mode:String="0", startPosition:Number=0, loop:Boolean=false, labels:Object=0){
super();
timeline.setLabels({});
setBounds(0,0,155,155);
// Layer_1
shape = new Shape();
shape.name = "shape";
shape.graphics.f("#00FF00").p("AojIkQjjjjAAlBQAAlADjjjQDjjjFAAAQFBAADjDjQDjDjAAFAQAAFBjjDjQjjDjlBAAQlAAAjjjjg");
shape.setTransform(77.5,77.5);
timeline.addTween(Tween.get(shape).wait(1));
}
}
} |
I don't think we've tried to add external JS to the minification. Normally the library represented by the typedef is loaded by the HTML template and the typedef only represents the API surface. In theory though, you should be able to build a Royale SWC and inject raw JS into the output folder so Google Closure Compiler will minify it. A Royale SWC for JS output is just compiled AS to represent the API surface and included JS files to be copied into the output folder. So, if you just write the API surface in GreenCircle.as:
You don't need any code bodies. Then you compile that into a SWC using only target=SWF and include-file the raw Animate .js file as GreenCircle.js The compiler should then compile your code against the AS APIs but then copy the included JS file into the output folder. The Royale Compiler doesn't care too much about the code format in the JS files in a SWC, but it does expect goog.provide, goog.require and goog.inherits, @constructor, @extends, @implements. If you can provide that the compiler will happily copy the .js file to the output folder. The key point is that the .js file does not need to be transpiled by the Royale Compiler. I don't know if Google Closure Compiler will accept the kind of wrapped JS function that Animate outputs. You may have to rewrite the JS API headers to use the simpler prototype style that Google Closure uses in their code. You may have to add goog.provide goog.require and goog.inherits and @extends, @ and other JSDoc to the raw Animate JS file. So, I think that is your trade-off. By porting the Animate JS to AS, the AS compiler will automate the annotation of the JS output for Closure. But you are porting from JS to AS and back to JS. If you just modify the API wrappings around the actual code in the JS output and create an empty AS API declaration you can skip the reading and writing of big files. I'm still going to look at the profiler to see what is taking so long and if there is any quick things we can do to make it faster. |
Oh and in case it wasn't clear, the same empty-method-body GreenCircle.as can be set up as a typedef by adding @externs and then your raw JS can be used untouched but it won't be minified. So that's another option. |
Thanks so much for the information. I've tried including other javascript in a swc after generating an as3 api for it in the past, but I believe where I messed up was not adding the typedefs to the javascript. So, it sounds like I've been somewhat on the right track, just missing a few pieces. Ultimately, I'll need to modify the resulting javascript from Animate for extended classes. If Animate would allow extending library items again, I wouldn't need to. I might be able to make this work if I can get it all wrapped up into a swc. It's important for it to be a singular javascript file in the end because I've developed a modular application to load different javascript files for each module as needed. This cuts way down on the initial load time. |
You can think of a SWC as a way to swap in JS for AS. In your app, AS will be transpiled and end up in the output. Any AS in a SWC does not, whatever you said was the JS representation for that AS class goes in the output. However, Google Closure Compiler may have some restrictions on the formatting of that JS file. If your goal is minification, you will probably need to have that JS comply with Google's code style which does allow inheritance. If your goal is only a single network fetch, I think you can just append the Animate JS files to the final minified .js file. At that point, JS is just JS. Only the browser is going to read it. Right now, by converting the JS to AS and back to JS, it isn't clear you are benefitting a lot from that double conversion. The body of the Green Circle constructor is going to be correct JS. Royale is mainly trying to help folks who are manually modifying AS get the best JS output and catch their mistakes early. |
I found one area that was a real bottleneck and improved it. I also cached a few more things but they were less impactful. If you are still using your original workflow, give it a try and see if you see faster compile times. |
@aharui thanks for the update. I'll definitely try it out. I'm also going see what I can do as far as making swc libraries from Animate. Do you have a link to an automated build with the change, or do I need to build the compiler source from develop branch? Thanks, |
Well, I tried to use the latest from develop. |
Nightly builds are available here However, the build failed last night because one change didn't consider a scenario. That should now be fixed and a new nightly build should be available in an hour or so. You can look for build failure/fixed emails on dev@royale or watch this status page and see if royale-asjs has been built since I posted this reply |
Excellent! Thanks @aharui |
@aharui Wow! your changes made a huge improvement. My big project that was 2-3 minutes is now 30 seconds. That is awesome. That will give me more options now. I'm still going to look at building a swc from the Animate export, but this will work until I get there. Thanks so much for your hard work and the information. It's truly appreciated. |
I realize you guys are still working towards a version one release, but the compiling time is painfully slow. For a sizeable project with 119 actionscript files, I'm seeing a compiling time of 2 - 3 minutes. I'm compiling with the debug flag and source maps. Is there a flag I'm missing? I really have to get this down or development time simply too long to not go with plain javascript.
Any help is much appreciated.
Thanks
The text was updated successfully, but these errors were encountered: