Высокопроизводительный способ компиляции регулярных выражений в AS3? - PullRequest
1 голос
/ 12 октября 2010

У меня довольно простой вопрос.Каков наилучший (наивысшая производительность / наименьшее использование памяти и т. Д.) Способ компиляции регулярного выражения в AS3?

Например, это:

private var expression:RegExp = new RegExp(".*a$");

private function modify():void {
    /* uses "expression" to chop up string */
}

Быстрее, чем это:

private var expression:RegExp = /.*a$/;

private function modify():void {
    /* uses "expression" to chop up string */
}

Также есть ли реальная необходимость сделать выражение переменной экземпляра, еслиЯ собираюсь использовать его только один раз?Например, какой из следующих блоков кода теоретически будет работать быстрее:

private var myRegEx:RegExp = /\n/;    

private function modify1():void {
    myString.split(/\n/);
}

private function modify2():void {
    myString.split(myRegEx);
}

Будет ли modify1 () работать с той же скоростью выполнения, что и modify2 ()?Я имею в виду, компилирует ли AS3 новый экземпляр RegExp в modify1 (), поскольку он не привязан к переменной экземпляра?

Любая помощь будет наиболее ценится:)

Ответы [ 2 ]

5 голосов
/ 14 октября 2010

Ваш тест не очень хорош. И вот почему:

  1. getTimer измеряет время, но время процессора действительно имеет значение. Если в какой-то момент по какой-то причине планировщик решит не запускать флэш-плеер, у вас будет меньше процессорного времени за тот же период времени. Вот почему результаты варьируются. Это лучшее, что вы можете использовать, но на самом деле не очень помогает, если вы пытаетесь отслеживать отклонения на несколько%.
  2. Отклонение действительно мало. Около 8%. Отчасти это связано с эффектом, описанным в пункте 1. Когда я проводил тесты, результаты действительно отличались. 8% могут прийти откуда угодно. Они могут даже зависеть от вашей машины, ОС или версии вспомогательного плеера или чего-либо еще. Результат не настолько важен, чтобы на него можно было положиться. Кроме того, ускорение на 8% не стоит учитывать, если только вы не обнаружите, что в вашей обработке строк есть ужасное узкое место, которое можно исправить тем или иным трюком с помощью RegExp s
  3. Самое важное: измеряемая вами разница не имеет ничего общего с регулярными выражениями, а только со всем остальным в тесте.

Позвольте мне объяснить это подробно.
Попробуйте public function test7():void{}. На моей машине это занимает около 30% -40% других тестов. Давайте немного чисел:

    Running Tests
    -------------------------------
    Testing method: test1, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01716ms
        Longest Iteration Time: 5ms
        Shortest Iteration Time: 0ms
        Total Test Time: 901ms
    -------------------------------
    Testing method: test2, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01706ms
        Longest Iteration Time: 5ms
        Shortest Iteration Time: 0ms
        Total Test Time: 892ms
    -------------------------------
    Testing method: test3, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01868ms
        Longest Iteration Time: 5ms
        Shortest Iteration Time: 0ms
        Total Test Time: 969ms
    -------------------------------
    Testing method: test4, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01846ms
        Longest Iteration Time: 5ms
        Shortest Iteration Time: 0ms
        Total Test Time: 966ms
    -------------------------------
    Testing method: test5, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01696ms
        Longest Iteration Time: 5ms
        Shortest Iteration Time: 0ms
        Total Test Time: 890ms
    -------------------------------
    Testing method: test6, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01696ms
        Longest Iteration Time: 5ms
        Shortest Iteration Time: 0ms
        Total Test Time: 893ms
    -------------------------------
    Testing method: test7, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.00572ms
        Longest Iteration Time: 1ms
        Shortest Iteration Time: 0ms
        Total Test Time: 306ms
    -------------------------------

Но почему? Следующие несколько вещей стоят дорого:

  • getTimer() медленный вызов глобальных функций (и статических методов других классов)
  • (tester[methodName] as Function).apply(); - это дорого. Доступ к динамическому свойству, требующий создания замыкания, затем приведение к анонимной функции и затем вызов через apply. Я не мог придумать более медленный способ вызова функции.
  • var tester:RegExpTester = new RegExpTester(); - создание экземпляра стоит дорого, поскольку требует выделения и инициализации.

следующий код будет работать значительно лучше. Служебная нагрузка, измеренная с помощью test7, на моей машине уменьшается на в 20 раза:

    private function test(methodName:String, iterations:int = 100):void {
        output("Testing method: " + methodName + ", " + iterations + " iterations...");    
        var start:Number = getTimer();
        var tester:RegExpTester = new RegExpTester();
        var f:Function = tester[methodName];
        for (var i:uint = 0; i < iterations; i++) f();//this call to f still is slower than the direct method call would be
        var wholeTime:Number = getTimer() - start;
        output("Test Complete.");
        output("\tAverage Iteration Time: " + (wholeTime / iterations) + "ms");
        output("\tTotal Test Time: " + wholeTime + "ms");
        output("-------------------------------");
    }

опять несколько цифр:

    Running Tests
    -------------------------------
    Testing method: test1, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01094ms
        Total Test Time: 547ms
    -------------------------------
    Testing method: test2, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01094ms
        Total Test Time: 547ms
    -------------------------------
    Testing method: test3, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01296ms
        Total Test Time: 648ms
    -------------------------------
    Testing method: test4, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01288ms
        Total Test Time: 644ms
    -------------------------------
    Testing method: test5, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01086ms
        Total Test Time: 543ms
    -------------------------------
    Testing method: test6, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.01086ms
        Total Test Time: 543ms
    -------------------------------
    Testing method: test7, 50000 iterations...
    Test Complete.
        Average Iteration Time: 0.00028ms
        Total Test Time: 14ms
    -------------------------------

так что теперь накладные расходы уменьшены до менее чем 1%, что делает их незначительными (хотя на самом деле их можно уменьшить намного больше). Однако сейчас отклонение составляет 16%. Это вдвое больше. И это начинает выглядеть немного яснее. Это все еще незначительно, ИМХО, но на самом деле оно указывает на два самых медленных метода: test3 и test4.

С чего бы это? Просто: оба метода создают новый объект RegExp (один использует литерал, другой - конструктор). Это потребляет разницу во времени, которую мы можем измерить. Теперь разница больше, так как раньше на каждую итерацию вы создавали 3 регулярных выражения (две переменные экземпляра инициализируются каждый каждый раз, когда создается экземпляр RegExpTester). Но разница, которая осталась сейчас, заключается в создании 50000 RegExp экземпляров. Все остальное примерно одинаково быстро.

Если в ответ на ваш вопрос необходимо сделать вывод: нет различий между литералами или построенными RegExp с. Поэтому я боюсь, что ответ таков: «Это не имеет значения, если вы помните общие правила оптимизации производительности». Надеюсь, это поможет.

2 голосов
/ 12 октября 2010

Для данного сценария я написал тестовый класс, который дает мне всю необходимую информацию о том, какой тип регулярного выражения использовать:

package {
    import flash.utils.getTimer;
    import flash.text.TextFormat;
    import flash.text.TextField;
    import flash.display.Sprite;
    public class RegExpTest extends Sprite {

        private var textfield:TextField;

        public function RegExpTest() {
            this.textfield = new TextField();
            this.textfield.x = this.textfield.y = 10;
            this.textfield.width = stage.stageWidth - 20;
            this.textfield.height = stage.stageHeight - 20;
            this.textfield.defaultTextFormat = new TextFormat("Courier New");

            this.addChild(textfield);

            this.runtests();
        }

        private function runtests():void {
            output("Running Tests");
            output("-------------------------------");
            test("test1", 50000);
            test("test2", 50000);
            test("test3", 50000);
            test("test4", 50000);
            test("test5", 50000);
            test("test6", 50000);
        }

        private function test(methodName:String, iterations:int = 100):void {
            output("Testing method: " + methodName + ", " + iterations + " iterations...");    

            var wholeTimeStart:Number = getTimer();
            var iterationTimes:Array = [];

            for (var i:uint = 0; i < iterations; i++) {
                var iterationTimeStart:Number = getTimer();

                var tester:RegExpTester = new RegExpTester();
                // run method.
                (tester[methodName] as Function).apply();

                var iterationTimeEnd:Number = getTimer(); 
                iterationTimes.push(iterationTimeEnd - iterationTimeStart);
            }

            var wholeTimeEnd:Number = getTimer();

            var wholeTime:Number = wholeTimeEnd - wholeTimeStart;

            var average:Number = 0;
            var longest:Number = 0;
                var shortest:Number = int.MAX_VALUE;

            for each (var iteration:int in iterationTimes) {
                average += iteration;

                if (iteration > longest)
                    longest = iteration;

                if (iteration < shortest)
                    shortest = iteration;
            }

            average /= iterationTimes.length;

            output("Test Complete.");
            output("\tAverage Iteration Time: " + average + "ms");
            output("\tLongest Iteration Time: " + longest + "ms");
            output("\tShortest Iteration Time: " + shortest + "ms");
            output("\tTotal Test Time: " + wholeTime + "ms");
            output("-------------------------------");
        }

        private function output(message:String):void {
            this.textfield.appendText(message + "\n");
        }

    }
}

class RegExpTester {

    private static const expression4:RegExp = /.*a$/;

    private static const expression3:RegExp = new RegExp(".*a$");

    private var value:String = "There is a wonderful man which is quite smelly.";

    private var expression1:RegExp = new RegExp(".*a$");

    private var expression2:RegExp = /.*a$/;

    public function RegExpTester() {

    }

    public function test1():void {
        var result:Array = value.split(expression1);
    }

    public function test2():void {
        var result:Array = value.split(expression2);
    }

    public function test3():void {
        var result:Array = value.split(new RegExp(".*a$"));
    }

    public function test4():void {
        var result:Array = value.split(/.*a$/);
    }

    public function test5():void {
        var result:Array = value.split(expression3);
    }

    public function test6():void {
        var result:Array = value.split(expression4);
    }

}

Результаты, полученные при выполнении этого примера, следующие:

Running Tests
-------------------------------
Testing method: test1, 50000 iterations...
Test Complete.
Average Iteration Time: 0.0272ms
Longest Iteration Time: 23ms
Shortest Iteration Time: 0ms
Total Test Time: 1431ms
-------------------------------
Testing method: test2, 50000 iterations...
Test Complete.
Average Iteration Time: 0.02588ms
Longest Iteration Time: 5ms
Shortest Iteration Time: 0ms
Total Test Time: 1367ms
-------------------------------
Testing method: test3, 50000 iterations...
Test Complete.
Average Iteration Time: 0.0288ms
Longest Iteration Time: 5ms
Shortest Iteration Time: 0ms
Total Test Time: 1498ms
-------------------------------
Testing method: test4, 50000 iterations...
Test Complete.
Average Iteration Time: 0.0291ms
Longest Iteration Time: 5ms
Shortest Iteration Time: 0ms
Total Test Time: 1495ms
-------------------------------
Testing method: test5, 50000 iterations...
Test Complete.
Average Iteration Time: 0.02638ms
Longest Iteration Time: 5ms
Shortest Iteration Time: 0ms
Total Test Time: 1381ms
-------------------------------
Testing method: test6, 50000 iterations...
Test Complete.
Average Iteration Time: 0.02666ms
Longest Iteration Time: 10ms
Shortest Iteration Time: 0ms
Total Test Time: 1382ms
-------------------------------

Интересно, мягко говоря.Кажется, что наш разброс на самом деле не слишком велик и что компилятор, вероятно, делает что-то закулисное для статической компиляции регулярных выражений.Пища для размышлений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...