    /**
     * @class Ext.perf.Monitor
     * @singleton
     * @private
     */
    Ext.define('Ext.perf.Monitor', {
        singleton: true,
        alternateClassName: 'Ext.Perf',

        requires: [
            'Ext.perf.Accumulator'
        ],

        constructor: function () {
            this.accumulators = [];
            this.accumulatorsByName = {};
        },

        calibrate: function () {
            var accum = new Ext.perf.Accumulator('$'),
                total = accum.total,
                getTimestamp = Ext.perf.Accumulator.getTimestamp,
                count = 0,
                frame,
                endTime,
                startTime;

            startTime = getTimestamp();

            do {
                frame = accum.enter();
                frame.leave();
                ++count;
            } while (total.sum < 100);

            endTime = getTimestamp();

            return (endTime - startTime) / count;
        },

        get: function (name) {
            var me = this,
                accum = me.accumulatorsByName[name];

            if (!accum) {
                me.accumulatorsByName[name] = accum = new Ext.perf.Accumulator(name);
                me.accumulators.push(accum);
            }

            return accum;
        },

        enter: function (name) {
            return this.get(name).enter();
        },

        monitor: function (name, fn, scope) {
            this.get(name).monitor(fn, scope);
        },

        report: function () {
            var me = this,
                accumulators = me.accumulators,
                calibration = me.calibrate();

            accumulators.sort(function (a, b) {
                return (a.name < b.name) ? -1 : ((b.name < a.name) ? 1 : 0);
            });

            me.updateGC();

            Ext.log('Calibration: ' + Math.round(calibration * 100) / 100 + ' msec/sample');
            Ext.each(accumulators, function (accum) {
                Ext.log(accum.format(calibration));
            });
        },

        getData: function (all) {
            var ret = {},
                accumulators = this.accumulators;

            Ext.each(accumulators, function (accum) {
                if (all || accum.count) {
                    ret[accum.name] = accum.getData();
                }
            });

            return ret;
        },

        updateGC: function () {
            var accumGC = this.accumulatorsByName.GC,
                toolbox = Ext.senchaToolbox,
                bucket;

            if (accumGC) {
                accumGC.count = toolbox.garbageCollectionCounter || 0;

                if (accumGC.count) {
                    bucket = accumGC.pure;
                    accumGC.total.sum = bucket.sum = toolbox.garbageCollectionMilliseconds;
                    bucket.min = bucket.max = bucket.sum / accumGC.count;
                    bucket = accumGC.total;
                    bucket.min = bucket.max = bucket.sum / accumGC.count;
                }
            }
        },

        watchGC: function () {
            Ext.perf.getTimestamp(); // initializes SenchaToolbox (if available)

            var toolbox = Ext.senchaToolbox;

            if (toolbox) {
                this.get("GC");
                toolbox.watchGarbageCollector(false); // no logging, just totals
            }
        },

        setup: function (config) {
            if (!config) {
                config = {
                    /*insertHtml: {
                        'Ext.dom.Helper': 'insertHtml'
                    },*/
                    /*xtplCompile: {
                        'Ext.XTemplateCompiler': 'compile'
                    },*/
    //                doInsert: {
    //                    'Ext.Template': 'doInsert'
    //                },
    //                applyOut: {
    //                    'Ext.XTemplate': 'applyOut'
    //                },
                    render: {
                        'Ext.AbstractComponent': 'render'
                    },
    //                fnishRender: {
    //                    'Ext.AbstractComponent': 'finishRender'
    //                },
    //                renderSelectors: {
    //                    'Ext.AbstractComponent': 'applyRenderSelectors'
    //                },
    //                compAddCls: {
    //                    'Ext.AbstractComponent': 'addCls'
    //                },
    //                compRemoveCls: {
    //                    'Ext.AbstractComponent': 'removeCls'
    //                },
    //                getStyle: {
    //                    'Ext.core.Element': 'getStyle'
    //                },
    //                setStyle: {
    //                    'Ext.core.Element': 'setStyle'
    //                },
    //                addCls: {
    //                    'Ext.core.Element': 'addCls'
    //                },
    //                removeCls: {
    //                    'Ext.core.Element': 'removeCls'
    //                },
    //                measure: {
    //                    'Ext.layout.component.Component': 'measureAutoDimensions'
    //                },
    //                moveItem: {
    //                    'Ext.layout.Layout': 'moveItem'
    //                },
    //                layoutFlush: {
    //                    'Ext.layout.Context': 'flush'
    //                },
                    layout: {
                        'Ext.layout.Context': 'run'
                    }
                };
            }

            this.currentConfig = config;

            var key, prop,
                accum, className, methods;
            for (key in config) {
                if (config.hasOwnProperty(key)) {
                    prop = config[key];
                    accum = Ext.Perf.get(key);

                    for (className in prop) {
                        if (prop.hasOwnProperty(className)) {
                            methods = prop[className];
                            accum.tap(className, methods);
                        }
                    }
                }
            }

            this.watchGC();
        }
    });