Skip to content
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

Closed
kennylerma opened this issue Mar 25, 2018 · 27 comments
Closed

Slow Compiler Build Time #36

kennylerma opened this issue Mar 25, 2018 · 27 comments
Assignees
Milestone

Comments

@kennylerma
Copy link

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

@aharui
Copy link
Contributor

aharui commented Mar 26, 2018

Hi Kenny,
Thanks for trying Royale.

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.

@kennylerma
Copy link
Author

kennylerma commented Mar 26, 2018

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
Running process: C:\Program Files (x86)\FlashDevelop\Tools\fdbuild\fdbuild.exe "C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\PassFailVersionHTML5Conversion.as3proj" -ipc 01482cc6-bbef-4091-b1f1-4c2f1092411f -version "4.14.1" -compiler "D:\flexjssdk" -library "C:\Program Files (x86)\FlashDevelop\Library"
Building PassFailVersionHTML5Conversion
Running Pre-Build Command Line...
cmd: build_fd.bat D:\flexjssdk PassFailVersionHTML5Conversion debug

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"
Using Flex SDK: D:\flexjssdk
MXMLJSC
-js-output-type=jsc
+configname=js
src\FinalPuppet_PassFailVersion_Main.as
-external-library-path+=libs-external*
-debug=true
-external-library-path+=D:\flexjssdk\js\libs\js.swc
Compiling file: C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\bin\js-debug\FinalPuppet_PassFailVersion_Main.js
Compiling file: C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\bin\js-debug\com\flagger\FinalPuppet_PassFailVersion\FinalPuppet_PassFailVersion_base.js
Compiling file: C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\bin\js-debug\com\flagger\PassFailMain.js
Compiling file: C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\bin\js-debug\flash\events\MouseEvent.js
Compiling file: C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\bin\js-debug\com\flagger\FinalPuppet_PassFailVersion\messagefinish_MC.js
Compiling file: C:\Users\Kenny\Desktop\PassFailVersionHTMLConeversion\bin\js-debug\com\flagger\FinalPuppet_PassFailVersion\container_MC.js

Sample Code - I have many files with many lines like the one below.

shape_1.graphics.f("#000000").p("AAHAjQgBgDACgFIAKgeQADgKgBgFQgBgEgHAAQgFAAgFADQgEACgEAGQgDAEgEAKIgIAYQgCAFgDADQgEADgEAAQgEAAgCgDQgBgDACgFIASg1QACgGADgCQADgDAEAAIAEABQABABAAAAQAAAAABABQAAAAAAABQAAAAAAABIAAAFIgBADQAGgHAHgDQAHgDAHAAQAIAAAEADQAFAEABAFQABAEgBAEIgDAKIgMAiQgCAFgDADQgEADgEAAQgEAAgCgDg");

@aharui
Copy link
Contributor

aharui commented Mar 27, 2018

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?

@Harbs
Copy link
Contributor

Harbs commented Mar 27, 2018

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?

@aharui
Copy link
Contributor

aharui commented Mar 27, 2018

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.

@aharui
Copy link
Contributor

aharui commented Mar 27, 2018

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"?

@aharui
Copy link
Contributor

aharui commented Mar 27, 2018

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.

@kennylerma
Copy link
Author

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.

@aharui
Copy link
Contributor

aharui commented Mar 27, 2018

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.

@kennylerma
Copy link
Author

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.
CompilerTest.zip

shape_3572 = new Shape();
shape_3572.name = "shape_3572";
shape_3572.graphics.f("rgba(3,1,1,0.992)").p("AGzKyIhaAAIgKAAIhGAAIgBgFQgxgFgyAAIgKAAIhaAAIgKAAIgBgFQgxgFgxAAIAAAKIgKAAIgUAAIgKAAIhkAAIgKAAIhkAAIgKAAIi+AAIgKAAIiWAAIAAgKIAAhQIAAgKIAAgyQASgggIg6IAAgKIAAgKIAAhaIAAgKIAAhaIAAgKIAAgKQARgggGg6IgBgKIAAgKIAAhaIAAgKIAAgnQARghgGg5IgBgKIAAgKIAAg8QARhDgGhdIgBgKQASghgHg5IgBgKIAAgKIAAgUQAOg9AGhFIAAgKQAlgwBnARIAKABQAyAAAygFIAAgFIAoAAIAKAAIBaAAIAKAAIBaAAIAKAAIBZAAIAKAAIB4AAIAKAAICCAAIAKAAIBaAAIAKAAQBBAABAAFIABAFQAAAFgCADQgDACgFAAIgKAAIAAAoIAAAKQgDBTBKAHIhHAAIAAAKIAAAKIgKAAIAABaIAAAKIAABaIAAAKIAABaIAAAKIAAAyIAAAKIgKAAIAABaIAAAKIAABPIAAAKIgKAAIAABaIAAAKIAABkIAAAKIAAAUIAAAKIgKAAIAABaIAAAKIAABaIAAAKIAAAKIgKAAIAACqIAAAKIAAAKIAAAKIgKAAgADXKUIAKAAQA+ASBYgIIAKAAQAwgHgRhKIgBgJIAKAAQAShOgIhmIAAgKIAAgKIAAgyIAAgKIAAgeQARglgGg/IgBgKIAAgKIAAg8QARhTgGhqIgBgKQARhdgGh1IgBgKIAAgKQASh7gIiTIAAgKIAAgKIAAg8IAAgFQkOgFkNAAIAAAKIgKAAIgeAAIgKAAIhkAAIgKAAIjSAAIAAAKIgKAAIhkAAIAAAKIgFAAQACAVgRgBIAAAyIAAAKIgKAAIgKAAIAABuIAAAKIAAAUIAAAKIgKAAIAABaIAAAKIAAAKIAAAKIgKAAQAABLAFBLIAFAAIAAAKIgKAAIAABkIAAAKIgKAAIAABtIAAAKIAAAoIAAAKIgKAAIAABkIAAAKIAABkIAAAKIAAAKIAAAKIgKAAQAAA3AFA2IAFABIAAAKIgKAAQAABQAFBQIAFAAIBaAAIAKAAIAoAAIAKAAQBDASBcgNIABgFIAKAAIAKAAQA6ASBSgNIAAgFQBBAABAgFIABgFIAdAAIAKAAQAqASBEgHIAKgBIBQAAgAKFoRIgUAAIgKAAIgWAAQAYgCAdgHIAJgBIAAAKIgKAAg");
shape_3572.setTransform(117.5,109);

@aharui
Copy link
Contributor

aharui commented Mar 28, 2018

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.

@kennylerma
Copy link
Author

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.

@aharui
Copy link
Contributor

aharui commented Mar 28, 2018

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.

@kennylerma
Copy link
Author

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;

@bigosmallm
Copy link
Contributor

Kenny, I was able to quickly put together an externs version of the JSFL library.
You can see it here: https://github.com/bigosmallm/royale_externs
Hope this helps.

@aharui
Copy link
Contributor

aharui commented Mar 28, 2018

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

@kennylerma
Copy link
Author

kennylerma commented Mar 29, 2018

@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));
		
		}
	}
}

@aharui
Copy link
Contributor

aharui commented Mar 29, 2018

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:

public class GreenCircle extends MovieClip
{
    public var shape:Shape;
    public function GreenCircle(mode:String="0", startPosition:Number=0, loop:Boolean=false)
    {
    }
}

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.

@aharui
Copy link
Contributor

aharui commented Mar 29, 2018

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.

@kennylerma
Copy link
Author

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.

@aharui
Copy link
Contributor

aharui commented Mar 29, 2018

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.

@aharui aharui closed this as completed in dbbcee5 Mar 30, 2018
@aharui
Copy link
Contributor

aharui commented Mar 30, 2018

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.

@kennylerma
Copy link
Author

@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,
Kenny

@kennylerma
Copy link
Author

Well, I tried to use the latest from develop.
I used Maven to build the develop branch and set the enviroment variable ROYALE_COMPILER_HOME to the compiler-jx folder path. However, I get an error.
Unable to access jarfile D:\royale-compiler-develop\compiler-jx\js\lib\mxmlc.jar

@aharui
Copy link
Contributor

aharui commented Mar 30, 2018

Nightly builds are available here

http://apacheroyaleci.westus2.cloudapp.azure.com:8080/job/royale-asjs/lastSuccessfulBuild/artifact/out/

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

http://apacheroyaleci.westus2.cloudapp.azure.com:8080

@kennylerma
Copy link
Author

Excellent! Thanks @aharui

@kennylerma
Copy link
Author

@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.

@carlosrovira carlosrovira added this to the 0.9.3 milestone Apr 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants