What I did to Mach II
There has been a lot of interest in the steps I took to optimize my client's website. The main interest was in what I did to Mach II in order to speed it up, especially as Mach II is rather optimized to start with. The answer is to cheat.
If you look at the Mach II code you'll see that it's rather complex. It's also 1000% legal. Every component is documented as is every cfcomponent and cffunction tag. Output="False" is used everywhere needed, access is always defined, every cffunction tag has a returntype and every cfargument tag has both a type and a required when needed. It is this attention to detail and legality that gives Mach II and every other CFC based framework it's unexpected overhead.
Let's start with the cffunction tag. The returntype attribute performs a data validation on the information being returned from the function. Any validation operation has a certain amount of overhead and when the validation is done against a component reference then the overhead becomes more noticable. In addition, I find the returntype to almost be an insult. If I wrote the function then I should have a relative clue as to what it is returning. If the returntype is only being used for documentation rather than for actual validation, then that information can be put in the hint.
But wait! I'm not saying to remove the returntype. It is actually rather important to the operation of a function. What it does beyond data validation is tell ColdFusion IF the function is returning anything. For this reason I always have a returntype defined but its value will either be any or void. Any basically tells ColdFusion that the function is returning something but that it should not worry about validating it. Void tells ColdFusion that the function will not return anything at all. If a cfreturn tag is used within the function then an error is thrown.
<cffunction name="getDefaultInvoker" access="public" returntype="MachII.framework.ListenerInvoker" output="false" hint="Returns an instance of the default invoker (EventInvoker) for this Listener.">
Modified cffunction tag
<cffunction name="getDefaultInvoker" access="public" returntype="any" output="false" hint="(MachII.framework.ListenerInvoker) Returns an instance of the default invoker (EventInvoker) for this Listener.">
The removal of the returntype alone causes a noticable speed increase for components that are heavily used. If we take the same idea of removing data validation and apply it to cfargument tags then we'll see even more of an improvement. There are actually 2 different types of data validation going on in a cfargument tag. The first is the same data type validation as defined above and this is defined by the type attribute. The second is a check for existance and is defined by the required attribute. The total removal of both of these attributes increases the savings we get from the removal of the returntype. I do one additional thing, though. I add a hint attribute to the tag, which will tell me what data type was expected and if it was required. This is for documentation purposes only, but does help when debugging.
<cfargument name="eventName" type="string" required="true" />
<cfargument name="eventHandler" type="MachII.framework.EventHandler" required="true" />
Modified cfargument tags
<cfargument name="eventName" hint="(required - string) what the data should be" />
<cfargument name="eventHandler" hint="required - component) MachII.framework.EventHandler being passed in" />
The removal of the type and/or required attributes from the cfargument tags is not without consequences. There are times where a function needs to be sure of the data being passed in. I judge these on a case by case basis, but when it comes to Mach II I didn't even bother. Mach II is so solid in its construction that every function is very exact in what gets passed to and from it.
As a final warning, remember that Mach II is a closed system that has been heavily tested and is only modified by a select few. No one is supposed to be touching the core code other than those select few. Because of this strict control, I felt very confident in my alterations and they worked flawlessly. In the end thats all that matters to a client...has the job been done.


Would it be fair to say that you did what you did with little or no worry because you knew "Mach II is so solid", that it is no longer that solid, having removed these compontent validation attributes?
Also, I imagine this would make it hard to update the framework when the next version was released as you'd effectively overwrite you optimisation.
Just some food for thought (for me) :)
As for new releases of Mach II, all I have to do is download it and then run a few regular expressions to rebuild all of my mods. :)
If my memory serves me correctly, I think it's Reactor that utilizes the sudo _type attribute. Basically by doing this, you can have the type set to Any and the _type set to the actual object type being returned. This is done for self documentation with the cfcexplorer and also so you don't have to go crazy with documenting it in the hints.
If I'm wrong about this, call me out on it.
http://cfrant.blogspot.com/2006/06/duck-typing-for...).
Instead we decided to modify several parts of the framework for performance reasons.
e.g we got rid off all commands (memory leaks), invokers (we always use the default invoker) and unneccessary parameters.
We also changed the Mach-II DTD, and the way views are registered and displayed.
I agree with Michael: Mach-II is a great, solid framework and this post should not be understood as criticism. And yes, I know that it will be harder to update to the next version of Mach-II, but I can live with that.
Bear in mind I'm coming from the perspective that Mach-II has been used on numerous large, high traffic applications and not once has anyone brought up memory leaks related to commands (or memory leaks at all for that matter). I would think if this was truly an issue within the framework core itself it would have come up before now, and you can certainly post your concerns to the Mach-II mailing list instead of making a buried reference in blog comments. We can't address problems (if indeed this is one) if no one tells us they're running into them, but given concrete examples of issues we absolutely will investigate.
And just so I'm clear, after the two blog posts about what you did to optimize, am I right in distilling it down to "you removed removed type and returntype"? I guess after the previous post I was expecting something more far-reaching. Am I missing something? Thanks.
Duck typing is - unfortunately - known to be a big performance boost, Fusebox and Model-Glue both found that. Mach II was supposedly producing a version with the core having no types. That was automatic as part of the build, according to something on Matt and Peter's podcast if I remember correctly.
As for Harry's claims about Mach II, I definitely want to see some backup there because it sounds like nonsense to me.
>> As for Harry's claims about Mach II, I definitely want to see some backup there because it sounds like nonsense to me.
I already answered Matt privatly, but here is another more detailed answer:
=======================================================
The main reason for not posting my changes to the Mach-II List, or directly to you or Peter, were my framework changes.
They work for us, but I am not sure if they work for everybody.
After reading Mike Schierberls posts
http://www.schierberl.com/cfblog/index.cfm/2006/10...
http://www.schierberl.com/cfblog/index.cfm/2006/10...
http://www.schierberl.com/cfblog/index.cfm/2006/10...
I started to test some big Mach-II applications with Jrockits MemLeaker tool.
We experienced that several Mach-II command (especially the viepagecommand.cfc) instances were kept in Application Scope and never freed. So we decided to rewrite some parts:
- We never needed some of the default properties so we removed all but 2:
<property name="applicationRoot" ...
<property name="defaultEvent" ...
- Views are included in event-handlers, the view part was removed:
<view-page page="ui/views/basics/basiclayout.cfm" />
- Invokers: we are only using the default invoker
- Commands: Mach-II builds a struct of event-handlers, containing an array of events. This array contains command instances.
The command logic was moved to eventhandler and eventmanager. Commands in our version are simple structs.
...
Your comments about changes to Mach II make no sense as far as performance and memory usage are concerned. Can you provide reliable evidence that you actually made any valid improvements?
I say this because macromedia.com - and adobe.com - has been relying on Mach II for about a quarter of its applications for years. If Mike's ideas were valid, the site wouldn't stay up for seconds whereas it has been rock solid as far as Mach II is concerned.
Your comments about the property values, the view and the invokers and commands make no sense at all and seem to have no basis in reality.
It was not my intention to criticise Mach-II (see my first post) or to praise my framework changes.
As I already said - Mach-II lite works for us, development was simplified (e.g. controller generation) and imo the performance has improved. This are valid improvements for us, but maybe not for the majority of Mach-II users.
Of course I could do some performance test with both versions, but my time is limited.
So I can only suggest to send my code to Peter and Matt, if they are interested. If not, I can live with that.
Building applications that do not fail is hard -- I would find it hard to justify the choice of making changes to a framework (e.g. ColdSpring) just so it works exactly how I would like it (like having the use the term bean all over the place ;-). The only thing you mentioned that some people have asked for is to do away with page-views and just include them into the event-handlers. However, I've found this feature to be useful, whereas we need to change widgets for marketing, etc. over a period of time -- we just change it in one place and it magically changes throughout our website. Your solution would involve a search/replace of all the places where it defined. Also, you never know what is in the works for Mach-2.0 ;-)
Also, I wanted to mention that all six of the "required" Mach-II properties are optional. Mach-II will set them to default values if you do not set them in your config file. So in a sense, I don't think your changes to the properties actually changed any behavior -- in essence it appears they work exactly the same.
As for the memory issues, I've done extensive load testing -- if there were memory leaks like that -- I'm sure a majority of my tests would fail after awhile. As Sean points out, Adobe/Macromedia.com runs/ran quite a few Mach-II applications -- I don't think they would still be using the framework if it had proven to be unstable.
I observed the 1.5 changes (not in detail yet) and agree that updates to this version would be time consuming.
I agree that framework changes should be considered with care and are not necessary in most cases.
In this case I was conviced that I made the right decision, because
memory leak problems were solved - maybe I was wrong because this errors were only existent in the JRockit JVM
our Mach-II controller generation was simplified - fewer properties, simpler views
simpler controllers made debugging easier
slight performance gains because of the changed command logic and reduced framework overhead (e.g. view xml parsing, no command objects, ...)
I will some make performance tests with the new Mach-II Version and my simplified version and inform you about this tests (if you are interested). Maybe I will reconsider my decision.
(10 users, test duration 1 minute, cf debugging on, trace plugin active)
You can download the test code here:
http://www.harryklein.name/machIIperftest.zip
It includes:
HelloMachII - simple MachII apps, no listeners, filters and only 1 view
HelloMachII_hkl (modified controller)
machII - version 1.5
machII_hkl - my modified version
Results:
Mach-II 1.5
===========================
User No. Avg. Click Time [ms]
1 417
2 356
3 427
4 418
5 385
6 418
7 368
8 414
9 416
10 402
Sum = 4021 ms
Avg = 402,1 ms
Modified Version
===========================
User No. Avg. Click Time [ms]
1 360
2 299
3 332
4 299
5 335
6 343
7 308
8 318
9 324
10 299
Sum = 3217 ms
Avg = 321,7 ms
FYI, A simple-ish page on a site of mine typically runs 13-78ms depending on load. With debugging on (with report execution times), that same page goes to 600-800ms on average. As you can see -- it would adversely affect load testing results.
Yes, I know that this should never be the case on productions systems
The reason for turning debugging was not to demonstrate the ridiculous overhead but to highlight the differences. My app also runs 10-15ms depending on load with debugging off.
Peter - of course debugging adversely affects load testing results. This was intended and is the case for both versions of the framework.
Of course I could do additional tests with complex applications.
But I think that I invested enough time and wonder why I should keep justifying.
I posted my test apps and both versions of the framework, you just need to unzip the files and test it by yourselves.
First off, the regular Mach II 1.5.0 version ran exactly the same speed regardless of the setting of MACHII_CONFIG_MODE which was odd. Harry's modified version ran substantially faster in production mode than it did in development mode (which I would have expected for the regular version). Any ideas here?
In *development* mode (reload every time - the config Harry provided by default), the difference in execution under heavy load was 5ms between the regular Mach II version and Harry's modified version.
Given that the overhead of an application model and real-world views would normally completely swamp the overhead of the framework, 5ms per request isn't a big deal.
Assuming the sort of speed up between development and production that I would have expected on the regular Mach II cores, the difference between that and the modified version would probably be 2-3ms per request.
So, there *is* definitely a speed improvement here but the question to everyone here is: is it worth changing the framework for a few milliseconds per request?
http://blog.maestropublishing.com/index.cfm?mode=e...
In terms of the jRockit memory tool, I'm not sure how the tool can be considered "discredited", the tool does an accurate job of identifying instance counts of objects in the heap. How it is used is another story. In the case of mach-ii, there are objects that are persisted in memory to increase performance, just because these objects are not released from the heap does not mean that it is necessarily a problem.
The tool can be useful because it can give insight into the heap that you may not be able to get with a default CF Jvm, but it should be used with a healthy understanding of what it is actually reporting. In my case, I used the tool to pinpoint unexpected behavior, then created test cases to eliminate outside factors as the root cause. Specifically testing under multiple combinations of JVMs (Sun 1.4, Sun 1.5, BEA 1.4, BEA 1.5) and CF installs (6.1, 7.01,7.02) Only after eliminating these factors was I able to confidently say the code was a problem. My point being that you should not rush to judgment based on a single test result.