Faster Sin and Cosine Through Lookup Tables

download Faster Sin and Cosine Through Lookup Tables

of 15

Transcript of Faster Sin and Cosine Through Lookup Tables

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    1/15

    JACKSONDUNSTAN.COM Mastering AS3Search G

    Home About

    XML Speed Even Faster Trig Through Inlining

    Faster Math.sin and Math.cosThrough Lookup Tables

    byjacksoninAS3

    Tags: cos, cosine, look up table, LUT, performance, sin, sine, trig, triglut, trigonometry

    Trigonometry functions like Math.sin and Math.cos are widely used in AS3

    games and other apps that make intensive use of graphics, sound, or physics.

    Todays article shows you a way to trade a little bit of memory for much faster

    trigonometry computations, which can seriously speed up any app that makes

    heavy use ofMath.sin, Math.cos, and the like. Keep reading for the class

    thatll help you do this, a demo app, performance testing, analysis, and more.

    (UPDATE: optimized thanks to(UPDATE: optimized thanks to(UPDATE: optimized thanks to(UPDATE: optimized thanks to skyboyskyboyskyboyskyboy))))

    Lookup tables are a common technique in programming. They involve doing

    expensive computations once, storing them in a table, and accessing them

    later. In AS3, the idea is that accessing the values stored in a Vector table is

    faster than computing the value and therefore you get a speed boost.

    For such a widely-used technique, it doesnt seem to be getting much usage in

    the AS3 world. I found a test on actionscript.org that uses lookup tables for

    Math.sin and shows a performance boost, but the lookup table can only be

    used with radians that happen to be whole numbers (integers) and is therefore

    not very useful and an unfair comparison to the robust Math.sin function.

    There is also an article on polygonal.de discussing the subject, but it doesnt

    provide full source, uses the relatively-slow Array because it was written

    before Vector debuted in Flash Player 10, and doesnt say specifically if

    lookup tables are used to achieve the alleged 14x performance boost. It does,

    however, have a cool demo app that inspired my demo app below.

    Due to this lack of existing lookup table work, I decided to make my own

    lookup table for trig functions: any function like Math.sin and Math.cos that

    takes parameters in the range [0,2pi). Here is the basic usage:

    // Make a lookup table for Math.sin with 2 decimal places of

    precision

    var sineLUT:TrigLUT = new TrigLUT(2,Math.sin);

    // Query with arbitrary radians, just like the original Math.sin

    result = sineLUT.val(-45.123);

    If you dont need to pass an arbitrary radians value, there are even faster

    functions that work as long as you can guarantee limits:

    MAY 16

    2011

    TIP OF THE WEEK E-MAIL

    EEEE----Mail AddressMail AddressMail AddressMail Address ::::

    Subscribe

    ARCHIVES

    ULY 2012

    UNE 2012

    MAY 2012

    APRIL 2012

    MARCH 2012

    FEBRUARY 2012

    ANUARY 2012

    DECEMBER 2011

    NOVEMBER 2011

    OCTOBER 2011

    SEPTEMBER 2011

    AUGUST 2011

    ULY 2011

    UNE 2011

    MAY 2011

    APRIL 2011

    MARCH 2011

    FEBRUARY 2011

    ANUARY 2011

    DECEMBER 2010

    NOVEMBER 2010

    OCTOBER 2010

    SEPTEMBER 2010

    AUGUST 2010

    ULY 2010

    UNE 2010

    MAY 2010

    APRIL 2010

    MARCH 2010

    FEBRUARY 2010

    ANUARY 2010

    DECEMBER 2009

    NOVEMBER 2009

    OCTOBER 2009

    SEPTEMBER 2009

    AUGUST 2009

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    2/15

    // Radians >= 0

    result = sineLUT.valPositive(45.123);

    // Radians on (-2pi,2pi)

    result = sineLUT.valNormalized(-4.123);

    // Radians on [0,2pi)

    result = sineLUT.valNormalizedPositive(4.123);

    The lookup table and precision variable are public, so you can avoid the slow

    function call by inlining them. The functions are small, so it wont bloat up

    your code or be too messy:

    // Inline version of TrigLUT.valNormalizedPositive

    result = sineLUT.table[int(4.123*sineLUT.pow)];

    For the ultimate speed boost when youre doing lots of lookups in one

    function, cache the lookup table and precision variable as local variables:

    var sineTable:Vector. = sineLUT.table;

    var sinePow:Number = sineLUT.pow;

    result = sineTable[int(4.123*sinePow)];

    You can even make a lookup table for arbitrary functions. Heres one for the

    square ofMath.sin with 2 decimal places of precision:

    var sineSquaredLUT:TrigLUT = new TrigLUT(2,function(r:Number):Number{r = Math.sin(r);return r*r;}

    );

    Use this lookup table the same exact way you used the Math.sin lookup table:

    sineSquaredLUT.val(-45.123);

    Now that you know how to use it, here is the source code for TrigLUT(TrigTrigTrigTrigonometry LLLLookUUUUp TTTTable):

    /*

    The MIT License

    Copyright (c) 2011 Jackson Dunstan

    Permission is hereby granted, free of charge, to any person

    obtaining a copy

    of this software and associated documentation files (the

    "Software"), to deal

    in the Software without restriction, including without limitation

    the rights

    to use, copy, modify, merge, publish, distribute, sublicense,and/or sell

    copies of the Software, and to permit persons to whom the Software

    is

    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be

    included in

    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

    EXPRESS OR

    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

    MERCHANTABILITY,

    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT

    SHALL THE

    ULY 2009

    UNE 2009

    CATEGORIES

    AS2

    AS3

    AVASCRIPT

    UNCATEGORIZED

    ACCOUNT

    REGISTER

    LOG IN

    ON-TOPIC SITES

    OA EBERT

    PAUCLAIR.NET

    BIT-101

    DESTROY TODAY

    ALECMCE.COM

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    3/15

    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

    OTHER

    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

    ARISING FROM,

    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER

    DEALINGS IN

    THE SOFTWARE.

    */

    package

    {

    /**

    * A lookup table for trig values (e.g. sine, cosine) to

    improve on the

    * performance of the static functions found in the Math class* @author Jackson Dunstan

    */

    publicclass TrigLUT

    {

    /** 2 * PI, the number of radians in a circle*/

    public static const TWO_PI:Number = 2.0*Math.PI;

    /** The static TWO_PI cached as a non-static field*/

    private const TWO_PI:Number = TrigLUT.TWO_PI;

    /** Table of trig function values*/

    publicvar table:Vector.;

    /** 10^decimals of precision*/

    publicvarpow:Number;

    /**

    * Make the look up table

    * @param numDigits Number of digits places of precision

    * @param mathFunc Math function to call to generate

    stored values.

    * Must be valid on [0,2pi).

    * @throws Error If mathFunc is null or invalid on [0,2pi)

    */

    publicfunction TrigLUT(numDigits:uint, mathFunc:Function)

    {

    varpow:Number = this.pow = Math.pow(10, numDigits);

    varround:Number = 1.0/pow;

    var len:uint = 1+this.TWO_PI*pow;

    var table:Vector. = this.table = newVector.(len);

    var theta:Number = 0;

    for (var i:uint = 0; i < len;++i)

    {

    table[i] = mathFunc(theta);theta += round;

    }

    }

    /**

    * Look up the value of the given number of radians

    * @param radians Radians to look up the value of

    * @return The value of the given number of radians

    */publicfunction val(radians:Number):Number

    {

    return radians >= 0?this.table[int((radians%this.TWO_PI)*this.pow)]

    :this.table[int((TWO_PI+radians%this.TWO_PI)

    *this.pow)];

    }

    /**

    * Look up the value of the given number of radians

    * @param radians Radians to look up the value of. Must

    be positive.

    * @return The sine of the given number of radians

    * @throws RangeError If radians is not positive

    */

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    4/15

    publicfunction valPositive(radians:Number):Number

    {

    returnthis.table[int((radians%this.TWO_PI)*this.pow)];

    }

    /**

    * Look up the value of the given number of radians

    * @param radians Radians to look up the value of. Must

    be on (-2pi,2pi).

    * @return The value of the given number of radians

    * @throws RangeError If radians is not on (-2pi,2pi)

    */

    publicfunction valNormalized(radians:Number):Number

    {return radians >= 0

    ?this.table[int(radians*this.pow)]

    :this.table[int((this.TWO_PI+radians)*this.pow)];

    }

    /**

    * Look up the value of the given number of radians

    * @param radians Radians to look up the value of. Must

    be on [0,2pi).

    * @return The value of the given number of radians

    * @throws RangeError If radians is not on [0,2pi)

    */

    publicfunction valNormalizedPositive(radians:Number):

    Number

    {returnthis.table[int(radians*this.pow)];

    }

    }

    }

    Since the whole point of this exercise is to get a performance boost, lets do

    some performance testing! Here is an app that compares the performance of

    Math.sin and Math.cos to TrigLUT:

    /*

    The MIT License

    Copyright (c) 2011 Jackson Dunstan

    Permission is hereby granted, free of charge, to any person

    obtaining a copy

    of this software and associated documentation files (the

    "Software"), to deal

    in the Software without restriction, including without limitation

    the rights

    to use, copy, modify, merge, publish, distribute, sublicense,

    and/or sell

    copies of the Software, and to permit persons to whom the Software

    is

    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be

    included inall copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

    EXPRESS OR

    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

    MERCHANTABILITY,

    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT

    SHALL THE

    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

    OTHER

    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

    ARISING FROM,

    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER

    DEALINGS IN

    THE SOFTWARE.

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    5/15

    */

    package

    {

    importflash.display.*;

    importflash.utils.*;

    importflash.text.*;

    /**

    * Performance test app for TrigLUT, the trig function lookup

    table

    * @author Jackson Dunstan

    */

    publicclass TrigLUTTest extendsSprite

    {privatevar __logger:TextField = newTextField();privatefunctionlog(msg:*):void { __logger.appendText

    (msg +"\n"); }

    publicfunction TrigLUTTest()

    {

    __logger.autoSize = TextFieldAutoSize.LEFT;

    addChild(__logger);

    const DIGITS:uint = 2;

    const STEP:Number = 0.05;var beforeTime:int;var afterTime:int;var lut:TrigLUT = new TrigLUT(DIGITS,Math.sin);

    var lutTable:Vector. = lut.table;var lutPow:Number = lut.pow;

    const REPS:int = 100000;

    const NUM:int = int(TrigLUT.TWO_PI / STEP);var theta:Number;var i:int;

    var j:int;

    var result:Number;

    const TWO_PI:Number = TrigLUT.TWO_PI;

    log("Function,Time");

    beforeTime = getTimer();

    for (i = 0; i < REPS;++i)

    {

    theta = 0;for (j = 0; j < NUM;++j)

    {

    result = Math.sin(theta);

    theta += STEP;

    }

    }

    afterTime = getTimer();

    log("Math.sin,"+ (afterTime-beforeTime));

    beforeTime = getTimer();for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = Math.cos(theta);

    theta += STEP;

    }

    }

    afterTime = getTimer();

    log("Math.cos,"+ (afterTime-beforeTime));

    beforeTime = getTimer();for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = lut.val(theta);

    theta += STEP;

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    6/15

    }

    }

    afterTime = getTimer();

    log("TrigLUT.val,"+ (afterTime-beforeTime));

    beforeTime = getTimer();

    for (i = 0; i < REPS;++i)

    {

    theta = 0;for (j = 0; j < NUM;++j)

    {

    result = lut.valPositive(theta);

    theta += STEP;

    }}

    afterTime = getTimer();

    log("TrigLUT.valPositive,"+ (afterTime-beforeTime));

    beforeTime = getTimer();

    for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = lut.valNormalized(theta);

    theta += STEP;

    }

    }

    afterTime = getTimer();log("TrigLUT.valNormalized,"+ (afterTime-beforeTime));

    beforeTime = getTimer();for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = lut.valNormalizedPositive(theta);

    theta += STEP;

    }

    }

    afterTime = getTimer();

    log("TrigLUT.valNormalizedPositive,"+ (afterTime-

    beforeTime));

    beforeTime = getTimer();

    for (i = 0; i < REPS;++i)

    {

    theta = 0;for (j = 0; j < NUM;++j){

    result = theta >= 0

    ? lutTable[int((theta%TWO_PI)*lutPow)]

    : lutTable[int((TWO_PI+theta%TWO_PI)*lutPow)];

    theta += STEP;

    }

    }

    afterTime = getTimer();log("inline val,"+ (afterTime-beforeTime));

    beforeTime = getTimer();for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = lutTable[int((theta%TWO_PI)*lutPow)];

    theta += STEP;

    }

    }

    afterTime = getTimer();

    log("inline valPositive,"+ (afterTime-beforeTime));

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    7/15

    beforeTime = getTimer();

    for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = theta >= 0

    ? lutTable[int(theta*lutPow)]: lutTable[int((TWO_PI+theta)*lutPow)];

    theta += STEP;

    }

    }

    afterTime = getTimer();

    log("inline valNormalized,"+ (afterTime-beforeTime));

    beforeTime = getTimer();

    for (i = 0; i < REPS;++i)

    {

    theta = 0;

    for (j = 0; j < NUM;++j)

    {

    result = lutTable[int(theta*lutPow)];

    theta += STEP;

    }

    }

    afterTime = getTimer();

    log("inline valNormalizedPositive,"+ (afterTime-

    beforeTime));

    }}

    }

    Here is the test environment I ran this app on:

    Flex SDK (MXMLC) 4.1.0.16076, compiling in release mode (no debugging

    or verbose stack traces)

    Release version of Flash Player 10.3.181.14

    2.4 Ghz Intel Core i5

    Mac OS X 10.6.7

    And here are the results I got:

    FUNCTION TIME

    Math.sin 509

    Math.cos 509

    TrigLUT.val 464

    TrigLUT.valPositive 447

    TrigLUT.valNormalized 253

    TrigLUT.valNormalizedPositive 214

    inline val 336

    inline valPositive 329

    inline valNormalized 145

    inline valNormalizedPositive 133

    Here are the same results in graph form:

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    8/15

    These results clearly show the performance benefits ofTrigLUT:

    TrigLUT is always faster than Math.sin and Math.cos

    Reducing the acceptable radian values helps performance a lot, especially

    guaranteeing positive values

    Inlining lookups results in maximum performance

    In the best case, TrigLUT beats the Math class functions by almost 5x!5x!5x!5x!

    With such compelling results, we should make sure that they are still accurate.

    and not wildly different than the results we would normally get from the Math

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    9/15

    class functions. TrigLUT allows you to set the number of digits of precision

    youd like. The more digits you want, the bigger and more accurate the lookup

    table gets. Its important to remember though that increasing precision does

    notnotnotnot slow downTrigLUT. Usually, I find that two digits of precision (a lookup

    table with 628 Numbers = 4.9KB of memory) is enough for most uses, but I

    leave the precision up to each user ofTrigLUT to determine their own needs.

    Here is the source code for a small demo app that simply draws a sine or

    cosine curve using the Math class functions and the approximation via

    TrigLUT over the top. A live demo follows.

    /*

    The MIT License

    Copyright (c) 2011 Jackson Dunstan

    Permission is hereby granted, free of charge, to any person

    obtaining a copy

    of this software and associated documentation files (the

    "Software"), to deal

    in the Software without restriction, including without limitation

    the rights

    to use, copy, modify, merge, publish, distribute, sublicense,

    and/or sell

    copies of the Software, and to permit persons to whom the Software

    is

    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be

    included in

    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

    EXPRESS OR

    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

    MERCHANTABILITY,

    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT

    SHALL THE

    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

    OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

    ARISING FROM,

    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER

    DEALINGS IN

    THE SOFTWARE.

    */

    package

    {

    importflash.display.*;

    importflash.events.*;

    importflash.text.*;

    importflash.geom.*;

    /**

    * Demonstration app for TrigLUT, the trig function lookuptable

    * @author Jackson Dunstan

    */

    [SWF(width=640,height=480,backgroundColor=0xEEEAD9)]publicclass TrigLUTDemo extendsSprite

    {

    private static const TEXT_FORMAT:TextFormat = new

    TextFormat("_sans",11);

    privatevar bmd:BitmapData;privatevar bmdRect:Rectangle;

    privatevar mathFuncName:String;

    privatevar digits:uint;

    privatevar mathFuncFrame:Sprite;

    privatevar digitsFrame:Sprite;

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    10/15

    publicfunction TrigLUTDemo()

    {

    stage.scaleMode = StageScaleMode.NO_SCALE;

    stage.align = StageAlign.TOP_LEFT;

    this.bmd = newBitmapData(stage.stageWidth,

    stage.stageHeight,false, 0xffffffff);

    addChild(newBitmap(bmd));this.bmdRect = newRectangle(0,0,this.bmd.width,

    this.bmd.height);

    this.mathFuncFrame = addChoices(

    "Math function: ",onMathFuncButton,0,

    ["sin","cos"]

    );

    this.digitsFrame = addChoices(

    "Digits of precision: ",

    onPrecisionButton,

    this.mathFuncFrame.height,

    ["0","1","2","3","4","5"]

    );

    this.digits = 2;this.mathFuncName = "sin";

    redraw();

    var about:TextField = newTextField();

    about.defaultTextFormat = TEXT_FORMAT;

    about.y = this.digitsFrame.y+this.digitsFrame.height;

    about.htmlText = "TrigLUT Demo by JacksonDunstan.com\n"+"Black line: trig function\n"+"Red line: lookup table";

    about.autoSize = TextFieldAutoSize.LEFT;

    about.selectable = false;

    addChild(about);

    }

    privatefunction addChoices(

    promptStr:String,

    callback:Function,

    y:Number,

    choices:Array

    ):Sprite{

    var prompt:TextField = newTextField();

    prompt.defaultTextFormat = TEXT_FORMAT;

    prompt.text = promptStr;

    prompt.autoSize = TextFieldAutoSize.LEFT;

    prompt.selectable = false;

    addChild(prompt);

    varx:Number = prompt.width;const PAD:Number = 3;foreach (var choiceName:Stringin choices)

    {

    var tf:TextField = newTextField();

    tf.defaultTextFormat = TEXT_FORMAT;

    tf.name = "label";

    tf.text = choiceName;

    tf.autoSize = TextFieldAutoSize.LEFT;

    tf.selectable = false;

    tf.x = tf.y = PAD;

    var button:Sprite = newSprite();

    button.name = choiceName;

    button.graphics.beginFill(0xE6E2D1);

    button.graphics.drawRect(0,0, tf.width+PAD*2,

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    11/15

    tf.height+PAD*2);

    button.graphics.endFill();

    button.addChild(tf);

    button.addEventListener(

    MouseEvent.CLICK,

    function(ev:MouseEvent):void

    {

    var button:Sprite = ev.currentTargetas

    Sprite;var label:TextField = button.getChildByName

    ("label") asTextField;

    callback(label.text);

    }

    );button.x = x;

    button.y = y;

    addChild(button);

    x+= button.width+PAD;

    }

    prompt.y = y+ (button.height- prompt.height) /2;

    varframe:Sprite = newSprite();

    frame.graphics.lineStyle(1);

    frame.graphics.drawRect(0,0, tf.width+PAD*2,

    tf.height+PAD*2);

    addChild(frame);

    returnframe;

    }

    privatefunction onMathFuncButton(label:String):void

    {

    this.mathFuncName = label;

    redraw();

    }

    privatefunction onPrecisionButton(label:String):void

    {

    this.digits = int(label);

    redraw();

    }

    privatefunction redraw():void

    {

    var mathFuncButton:Sprite = getChildByName

    (this.mathFuncName) asSprite;

    this.mathFuncFrame.x = mathFuncButton.x;this.mathFuncFrame.y = mathFuncButton.y;

    var digitsButton:Sprite = getChildByName(String

    (this.digits)) asSprite;

    this.digitsFrame.x = digitsButton.x;this.digitsFrame.y = digitsButton.y;

    var mathFunc:Function = Math[this.mathFuncName];

    var lut:TrigLUT = new TrigLUT(this.digits, mathFunc);

    var bmd:BitmapData = this.bmd;

    bmd.lock();var w:int = bmd.width;

    var h:int = bmd.height;

    bmd.fillRect(this.bmdRect, 0xEEEAD9);

    var halfH:int = h /2;var stepTheta:Number = TrigLUT.TWO_PI / w;var theta:Number = 0;for (varx:int = 0;x< w;++x)

    {

    for (vary:int = 0;y< h;++y)

    {

    bmd.setPixel(

    x,

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    12/15

    0

    halfH + mathFunc(theta)*halfH,

    0xff000000

    );

    bmd.setPixel(

    x,

    halfH + lut.valNormalizedPositive(theta)

    *halfH,

    0xffff0000

    );

    }

    theta += stepTheta;

    }

    bmd.unlock();

    }}

    }

    Here is a live version of the demo. Make sure you have JavaScript turned on and

    click Show Demo.

    Show Demo

    In short, TrigLUT offers improved performance for trig functions like

    Math.sin and Math.cos by using lookup tables. If anyone has any suggestions

    for improving TrigLUT or alternatives to lookup tables entirely, feel free to join

    in the discussion below in the comments. To get the performance test, demo

    app, and TrigLUT class all in one convenient ZIP, you can download TrigLUT

    here.

    Tweet

    Comments (11)

    Nice, a comparison with Joa Eberts FastMath sine and cosine functionwould also be interesting.

    http://code.google.com/p/apparat/source/browse/as3/Apparat.Library.AS3/

    r=c8b764b3a535184a604e9d2309655de99be1d8b8

    #1 byKarlon May 16th, 2011 Reply|Quote

    I considered FastMath while writing the article, but decided to leave it

    out since it doesnt use lookup tables. Still, it achieves the same goal

    by producing faster sine and cosine results and is therefore a

    possibility for a future article.

    #2byjacksonon May 16th, 2011 Reply|Quote

    PROTIP: cos is sin with an offset, so you can use the same table for both if

    you apply the offset for the lookups.

    #3by Henke37Henke37Henke37Henke37 on May 16th, 2011 Reply|Quote

    Indeed it is, but it makes the lookup slower. Id rather spend another

    4.9KB of memory and have unique tables. Even on mobile, its not

    much RAM.

    #4byjacksonon May 16th, 2011 Reply|Quote

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    13/15

    Interesting article (again)! I ones used a dictionary for this, which started

    empty, and gets filled by lazy instantiation. However after a while it was

    using a lot of memory, so thats slows the thing down too. I think it is

    clever to use rounded values to save ram.

    Maybe if you want more precision (dimension) you could calculate the

    average between the prev/next angle-items in the lookup array around the

    current returned item. I dont know if that helps anyway or if you loose toomuch speed on that.

    public const sinusList:Dictionary = newDictionary();

    publicfunction getSin(angleRadians:Number):Number

    {

    return sinusList[angleRadians] ||= Math.sin(angleRadians);

    }

    Thanks for this helpful article!

    #5byMarkon May 16th, 2011 Reply|Quote

    Glad to hear you enjoyed the article and that there are still more

    people looking for a way to improve their trig computation

    performance in AS3. :)

    I considered using some interpolation like in polygonal.de article I

    linked, but the lookup becomes much slower due to the extra Vector

    access and math. To get better precision, I think its worth trading a

    little more RAM. 3 decimal places is pretty extreme and its only 6283

    Numbers = 49KB. Pretty cheap (if you ask me) and with zero

    slowdown.

    I also considered storing the values in a Dictionary, but since

    Dictionary access is 3x slower than Vector access, I figured the

    overhead would more than outweigh the Number multiply (* pow) and

    int()explicit type conversion.

    #6byjacksonon May 16th, 2011 Reply|Quote

    I did a small rewrite to increase performance by removing the static lookup

    (instance instead) and the conditional branch: the result was 15% faster

    execution of val:

    Windows XP x86 10.3.181.14(VM: 1.4 cyclone) Release PlugIn

    5000000 loops; 1 test per loop

    val (new): 490ms

    val (old): 576ms

    Test value: [-160] -0.21667508038735275 -0.21667508038735275

    Test value: [+160] 0.2197836122251347 0.2197836122251347

    Using this:

    #7byskyboyon May 16th, 2011 Reply|Quote

  • 7/31/2019 Faster Sin and Cosine Through Lookup Tables

    14/15

    NameNameNameName (required)(required)(required)(required)

    EEEE----Mail (required,Mail (required,Mail (required,Mail (required, will not be published)will not be published)will not be published)will not be published)

    publicfunction val(radians:Number):Number {

    returnthis.table[int(((this.TWO_PI *int(radians