Summary:
I'm trying to find out if a single method can be executed twice in overlap when executing on a single thread. Or if two different methods can be executed in overlap, where when they share access to a particular variable, some unwanted behaviour can occur.
Ex of a single method:
var ball:Date;
method1 ():Date {
ball = new Date();
<some code here>
return ball;
}
Questions:
1) If method1 gets fired every 20ms using the event system, and the whole method takes more than 20ms to execute, will the method be executed again in overlap?
2) Are there any other scenarios in a single thread environment where a method(s) can be executed in overlap, or is the AVM2 limited to executing 1 method at a time?
Studies: I've read through https://www.adobe.com/content/dam/Adobe/en/devnet/actionscript/articles/avm2overview.pdf which explains that the AVM2 has a stack for running code, and the description for methods makes it seem that if there isn't a second stack, the stack system can only accomodate 1 method execution at a time. I'd just like to double check with the StackeOverflow experts to see for sure.
I'm dealing with some time sensitive data, and have to make sure a method isn't changing a variable that is being accessed by another method at the same time.
ActionScript is single-threaded; although, can support concurrency through ActionScript workers, which are multiple SWF applications that run in parallel.
There are asynchronous patterns, if you want a nested function, or anonymous function to execute within the scope chain of a function.
What I think you're referring to is how AVM2 executes event-driven code, to which you should research AVM2 marshalled slice. Player events are executed at the beginning of the slice.
Heavy code execution will slow frame rate.
It's linear - blocking synchronously. Each frame does not invoke code in parallel.
AVM2 executes 20 millisecond marshalled slices, which depending on frame rate executes user actions, invalidations, and rendering.
Related
My apologies for the insane question but is it possible to have something like:
var data = ss.getSheetByName("info").getDataRange().getValues();
called as a global (ie, before using any functions) and then having that data var be available to all the functions? My program mainly reads through arrays with the main sheet having over 5k rows with mult columns of info and another sheet having almost 15k rows/mult columns of info.
Was wondering if it would be any saver of time by doing it this way instead of calling it every time the function is called.
I've been using arrays because I hear it's quicker for Google to flip through it find a particular piece of data then it is of Objects, etc. Sorry if I am making ZERO sense here ;)
Code in the global scope of a Google Apps Script is loaded on every execution, meaning that if you declared a variable in the global-scope it will be "overwritten" on every execution.
It might help if you have a "main" function that calls several helper functions that require the same object instance but this might have a "slightly" negative effects if you have functions that don't require that object instance. If your spreadsheet takes a lot of time to be loaded then the negative effects will not be "slightly", they will be noticeable, importantly or even dramatically negative.
There is an external C++ function that is called from Tcl/Tk and does some stuff in a noticeable amount of time. Tcl caller has to get the result of that function so it waits until it's finished. To avoid blocking of GUI, that C++ function has some kind of event loop implemented in its body:
while (m_curSyncProc.isRunning()) {
const clock_t tm = clock();
while (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT) > 0) {} // <- stuck here in case of tkwait/vwait
// Pause for 10 ms to avoid 100% CPU usage
if (double(clock() - tm) / CLOCKS_PER_SEC < 0.005) {
nanosleep(10000);
}
}
Everything works great unless tkwait/vwait is in action in Tcl code.
For example, for dialogs the tkwait variable someVariable is used to wait Ok/Close/<whatever> button is pressed. I see that even standard Tk bgerror uses the same method (it uses vwait).
The problem is that once called Tcl_DoOneEvent does not return while Tcl code is waiting in tkwait/vwait line, otherwise it works well. Is it possible to fix it in that event loop without total redesigning of C++ code? Because that code is rather old and complicated and its author is not accessible anymore.
Beware! This is a complex topic!
The Tcl_DoOneEvent() call is essentially what vwait, tkwait and update are thin wrappers around (passing different flags and setting up different callbacks). Nested calls to any of them create nested event loops; you don't really want those unless you're supremely careful. An event loop only terminates when it is not processing any active event callbacks, and if those event callbacks create inner event loops, the outer event loop will not get to do anything at all until the inner one has finished.
As you're taking control of the outer event loop (in a very inefficient way, but oh well) you really want the inner event loops to not run at all. There's three possible ways to deal with this; I suspect that the third (coroutines) will be most suitable for you and that the first is what you're really trying to avoid, but that's definitely your call.
1. Continuation Passing
You can rewrite the inner code into continuation-passing style — a big pile of procedures that hands off from step to step through a state machine/workflow — so that it doesn't actually call vwait (and friends). The only one of the family that tends to be vaguely safe is update idletasks (which is really just Tcl_DoOneEvent(TCL_IDLE_EVENTS | TCL_DONT_WAIT)) to process Tk internally-generated alterations.
This option was your main choice up to Tcl 8.5, and it was a lot of work.
2. Threads
You can move to a multi-threaded application. This can be easy… or very difficult; the details depend on an examination of what you're doing throughout the application.
If going this route, remember that Tcl interpreters and Tcl values are totally thread-bound; they internally use thread-specific data so that they can avoid big global locks. This means that threads in Tcl are comparatively expensive to set up, but actually use multiple CPUs very efficiently afterwards; thread pooling is a very common approach.
3. Coroutines
Starting in 8.6, you can put the inner code in a coroutine. Almost everything in 8.6 is coroutine-aware (“non-recursive” in our internal lingo) by default (including commands you wouldn't normally think of, such as source) and once you've done that, you can replace the vwait calls with equivalents from the Tcllib coroutine package and things will typically “just work”. (For example, vwait var becomes coroutine::vwait var, and after 123 becomes coroutine::after 123.)
The only things that don't have direct replacements are tkwait window and tkwait visibility; you'll need to simulate those with waiting for a <Destroy> or <Visibility> event (the latter is uncommon as it is unsupported on some platforms), which you do by binding a trivial callback on those that just sets a variable that you can coroutine::vwait on (which is essentially all that tkwait does internally anyway).
Coroutines can become messy in a few cases, such as when you've got C code that is not coroutine-aware. The main places in Tcl where these come into play are in trace callbacks, inter-interpreter calls, and the scripted implementations of channels; the issue there is that the internal APIs these sit behind are rather complicated already (especially channels) and nobody's felt up to wading in and enabling a non-recursive implementation.
I'm at a bit of a loss here. I have an issue that I think may be due to mouse events taking precedence. I have a function f being invoked on mouse clicks - f does some work, then invokes another function g. Is it even possible that f runs, then another click happens - invoking f again - and then g is executed?
If my phrasing is hard to understand, I'll try to show what I think may be happening:
click1 ----- /-----------\
\ / \
f -- f-- g g
/ \ /
click2 ------------ / \--------
|---------------- timeline----------------------|
I can say for certain the issue only arises (out of ~50 slow and ~50 quick double-clicks) when clicking twice in very quick succession (and not always even then). I realize my figure may confuse more than it clarifies, but I'm not sure how else to communicate my thoughts. Any input is greatly appreciated!
AS3 is a single threaded code execution environment which will execute all relevant code as it present itself. if a click triggers the execution of a chain of methods, all those methods will run before any other code can be executed again. As a result there cannot be a race condition in the code execution of AS3 code because of it single threaded nature.
All events in AS3 are not a special case in that regard, when their listener triggers all its code is executed the same way and no other code can be executed until it's done.
Special cases are:
You can suspend the execution by using timers and such so execution of code will happen at a later time. In that case there's no guaranty the triggering of those timers will be in sync with their starting order.
Executing asynchronous command (like loading something), in that case there's no guaranty loading operations will happen in order either.
But those special cases are not violating the execution of code principle in AS3, all code execute in one thread so their cannot be overlapping of any kind.
I've been reading a bit about how to use the inline metadata when using the ASC 2.0 compiler.
However I can't find any source of info explaining why I should use them.
Anyone knows?.
Functions induce overhead in any programming language. Per ActionScript, when function execution begins, a number of objects and properties are created.
First, an activation object is created that stores the parameters and local variables declared in a function body. It's an internal mechanism that cannot be directly accessed.
Second, a scope chain is created that contains an ordered list of objects that Flash platform checks for identifier declarations. Every function that executes has a scope chain that is stored in an internal property.
Function closures maintain a snapshot of a function and its lexical environment.
Moving code inline reduces the creation of these objects, and how references are maintained on the stack. Per Flash, you may see 4x performance increase.
Of course, there are tradeoffs - without the inline keyword induces code complexity; as well, inlining code increasing the amount of bytecode. Besides larger applications, the virtual machine spends additional time verifying and JIT compiling.
To simplify, inline is some sort of copy/paste of code. Since method calls are expensive and cost execution time, using inline keyword will copy/paste the body of the method each time the method call is present in your code so the method call will be replaced by the body of the method instead. Since this is done at compilation time it will increase in theory the size of the resulting app (if an inline method is called 10 times its body will be copied and pasted 10 times) but since all calls will be replaced you will gain speed execution. This is of course only relevant for demanding code execution like loops running at each frame for example.
Context: a large AS3 application that may be suffering from frequent but unpredictable "stop the world" garbage collection pauses. When one does strike it can take 30s or more to conclude.
This does not occur in testing, however it may in production.
Question: Is there any logging available from the Flash VM that might be used to detect and record such events? I am drawing on my experience with Java here. I have read widely on the functioning of the Flash GC machinery (reference-counting with mark/sweep), but I'm looking for some real telemetry from an application running in the wild as I understand that a mark/sweep GC event can "stop the world".
You are correct. The garbage collector pauses application execution.
The Flash Runtime garbage collector algorithm runs incrementally while marking memory in use. It pauses application execution when collecting unused portions of memory. The pause that occurs as the incremental collection cycle finishes can be longer than desired and can be observable or audible in some programs.
from reference
Check if garbage collector did run
As to my knowledge there is no direct way to know if GC ran. But it is possible to have a test function execute on ENTER_FRAME and check if the garbage has been collected since the last function call. Effectively since the last frame.
Through a Dictionary which can store weak referenced keys it is possible to see if an object has been collected. If this is the case garbage collection must have run. In the following class I create a new object, to later check if it has been collected.
package
{
import flash.display.Sprite;
import flash.utils.Dictionary;
public class GCTest
{
private static var dict:Dictionary = null;
public static function didGCRun():Boolean
{
if ( dict == null ) {
dict = new Dictionary(true);
}
var hasKeys:Boolean = false;
for ( var obj:* in dict ) {
hasKeys = true;
break;
}
if ( hasKeys ) {
return false;
}
else {
dict[new Sprite()] = null;
return true;
}
}
}
}
By checking on each frame you will know if gc stroke
addEventListener(Event.ENTER_FRAME, onEnterFrame);
private function onEnterFrame(event:Event):void
{
var gcRan:Boolean = GCTest.didGCRun();
}
Memory usage
You can also monitor garbage collection by checking memory usage. The documentation advises to use System.freeMemory()
The amount of memory (in bytes) that is allocated to Adobe Flash Player or Adobe AIR and that is not in use. This unused portion of allocated memory (System.totalMemory) fluctuates as garbage collection takes place. Use this property to monitor garbage collection.
I would use this value in conjunction with System.totalMemoryNumber()
Validate execution times
In conjunction with the other methods it might be helpful to record the actual frame rate or the execution time of code blocks. This can be achieved by storing the programs "uptime" in a variable and comparing it at a later point.
Use getTimer()
For a Flash runtime processing ActionScript 3.0, this method returns the number of milliseconds that have elapsed since the Flash runtime virtual machine for ActionScript 3.0 (AVM2) started.
var startTime:int = getTimer();
// execution or frame change
var executionTime:int = getTimer() - startTime;
when used on each frame you can compare this to stage.frameRate and check for discrepancies.
Advise garbage collector to execute
A possibility to mitigate your pauses might be to advise the garbage collector to execute manually. System.pauseForGCIfCollectionImminent(imminence:Number = 0.75) will pause program execution if the actual imminence is higher than the arguments value.
Imminence is defined as how far through marking the collector believes it is, and therefore how close it is to triggering a collection pause. The imminence argument to this function is a threshold: the garbage collector will be invoked only if the actual imminence exceeds the threshold value. Otherwise, this call returns immediately without taking action.
By calling this function with a low imminence value, the application indicates that it is willing to accept that a relatively large amount of marking must be completed. A high imminence value, on the other hand, indicates that the application should be paused only if marking is nearly complete. Typically, pauses are longer in the former case than in the latter.
imminence:Number (default = 0.75) — A number between 0 and 1, where 0 means less imminent and 1 means most imminent. Values less than 0 default to 0.25. Values greater than 1.0 default to 1.0. NaN defaults to 0.75
from reference
Adobe Scout is a nice tool to profile AS3 projects.
You can use :
setInterval( function()
{
trace(System.totalMemory);
},1000);
whenever garbage collector works and it's busy the amount of memory changes a lot. if you see the memory changes and at the same time your program paused or has glitches so it has been caused by GC.
first of all, what is your production environment ?
flash player, AIR desktop, AIR mobile ?
Your app is supposed to perform computation heavy tasks ?
30+ seconds of garbage collecting seems huge, but if the pauses are indeed caused by the garbage collector, logging it will not help you much, except confirming your supposition.
The first thing I would do, is extensively test your app with telemetry
If you see any sign of abnormal activity of the GC (big red spikes, or a permanent gc noise that is more than a couple of percents on every frame), activate memory allocation telemetry and find/eliminate bottlenecks in memory instantiations. Just remember that even freed allocations have an impact on gc so use pooling, and be very strict on allocations that occur on every frames, even a few allocations will have a significant impact.
Beware that parsing big xml or json files is very heavy on allocations.
Once you have made sure that your application is using memory properly, check (or let your testers/beta testers/users check) if the problem is still here or if it's less frequent or even has disapeared.
If it does't work, you will have at least improved the performance and memory footprint of your app in the first place
Don't you have more information on the nature of the pause and have you considered other options than GC ? Maybe the probleme is not where you search it :
You can try catching and logging all errors with UncaughtErrorEvent (then use URLLoader to post the error stack to your error logging service). It could help you detect hard to reproduce errors in production, for instance:
stuck in a function: should throw a ScriptTimeoutError after 15 seconds (code #1502) which will open a popup in flash player debugger.
any other Error will interrupt the whole stack which may put your application in an unresponsive state. Maybe some long timeout is making it responsive again ?
BTW How do you know that you application is experiencing 30+ seconds freeze if you are not logging it and have never reproduced it ? If it is a bug reported by end users, try to get more informations (get their os, flash player version, pattern...) If it's frequent, you should be able to reproduce it.