Wiki

Changes between Version 1 and Version 2 of RunTimePerformance

Show
Ignore:
Timestamp:
02/11/17 06:02:12 (8 years ago)
Author:
torial
Comment:

Added an example of changes made from a perf perspective

Legend:

Unmodified
Added
Removed
Modified
  • RunTimePerformance

    v1 v2  
    88 * don't optimize if you don't need to, root of all evil 
    99 
     10== Example == 
     11From the discussion thread in the forums (Why so slow?), some modified versions of the code are available for comparison: 
     12 * With Python 3.6.0, running against a slightly modified version (for tracking time) with 500,000 loops yielded a run-time of 0.66819638 seconds. 
     13 * An initial port (which included substantial refactoring to identify the cause of the different final results) with 50,000 loops yielded a run-time of 98.284 seconds 
     14 * After numerous attempts at speed improvement, the cobra code was sped up to a run-time of 0.057 seconds (or 11x faster than python, or 1,724x faster than original implementation. 
     15 
     16Some of the strategies attempting are listed below and whether they yielded much of a performance change: 
     17 * Made sure the the for loop variables had explicit types: yielded 33x improvement 
     18 * Used float instead of decimal: yielded 7x improvement 
     19 * Using multiplication instead of division: yielded 1.2x improvement 
     20 * Converted Math.pow to Math.sqrt for square roots: yielded 1.05x improvement 
     21 * Moved variable declarations into function-level scope: NO IMPROVEMENT 
     22 * Replaced properties with public fields: 1.6x improvement 
     23 
    1024== Discussions == 
    1125 
     
    1428 * [http://cobra-language.com/forums/viewtopic.php?f=4&t=700 Why so slow?] 2010-12 
    1529 * [http://cobra-language.com/forums/viewtopic.php?f=4&t=692 Cobra vs Python - speed] 2010-11 
     30 
     31---- 
     32 
     33*Python Code:* 
     34 
     35{{{ 
     36# The Computer Language Benchmarks Game 
     37# http://shootout.alioth.debian.org/ 
     38# 
     39# originally by Kevin Carson 
     40# modified by Tupteq, Fredrik Johansson, and Daniel Nanz 
     41# modified by Maciej Fijalkowski 
     42# 2to3 
     43 
     44import sys 
     45 
     46def combinations(l): 
     47    result = [] 
     48    for x in range(len(l) - 1): 
     49        ls = l[x+1:] 
     50        for y in ls: 
     51            result.append((l[x],y)) 
     52    return result 
     53 
     54PI = 3.14159265358979323 
     55SOLAR_MASS = 4 * PI * PI 
     56DAYS_PER_YEAR = 365.24 
     57 
     58BODIES = { 
     59    'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS), 
     60 
     61    'jupiter': ([4.84143144246472090e+00, 
     62                 -1.16032004402742839e+00, 
     63                 -1.03622044471123109e-01], 
     64                [1.66007664274403694e-03 * DAYS_PER_YEAR, 
     65                 7.69901118419740425e-03 * DAYS_PER_YEAR, 
     66                 -6.90460016972063023e-05 * DAYS_PER_YEAR], 
     67                9.54791938424326609e-04 * SOLAR_MASS), 
     68 
     69    'saturn': ([8.34336671824457987e+00, 
     70                4.12479856412430479e+00, 
     71                -4.03523417114321381e-01], 
     72               [-2.76742510726862411e-03 * DAYS_PER_YEAR, 
     73                4.99852801234917238e-03 * DAYS_PER_YEAR, 
     74                2.30417297573763929e-05 * DAYS_PER_YEAR], 
     75               2.85885980666130812e-04 * SOLAR_MASS), 
     76 
     77    'uranus': ([1.28943695621391310e+01, 
     78                -1.51111514016986312e+01, 
     79                -2.23307578892655734e-01], 
     80               [2.96460137564761618e-03 * DAYS_PER_YEAR, 
     81                2.37847173959480950e-03 * DAYS_PER_YEAR, 
     82                -2.96589568540237556e-05 * DAYS_PER_YEAR], 
     83               4.36624404335156298e-05 * SOLAR_MASS), 
     84 
     85    'neptune': ([1.53796971148509165e+01, 
     86                 -2.59193146099879641e+01, 
     87                 1.79258772950371181e-01], 
     88                [2.68067772490389322e-03 * DAYS_PER_YEAR, 
     89                 1.62824170038242295e-03 * DAYS_PER_YEAR, 
     90                 -9.51592254519715870e-05 * DAYS_PER_YEAR], 
     91                5.15138902046611451e-05 * SOLAR_MASS) } 
     92 
     93 
     94SYSTEM = list(BODIES.values()) 
     95PAIRS = combinations(SYSTEM) 
     96 
     97 
     98def advance(dt, n, bodies=SYSTEM, pairs=PAIRS): 
     99 
     100    for i in range(n): 
     101        for (([x1, y1, z1], v1, m1), 
     102             ([x2, y2, z2], v2, m2)) in pairs: 
     103            dx = x1 - x2 
     104            dy = y1 - y2 
     105            dz = z1 - z2 
     106            mag = dt * ((dx * dx + dy * dy + dz * dz) ** (-1.5)) 
     107             
     108            b1m = m1 * mag 
     109            b2m = m2 * mag 
     110            v1[0] -= dx * b2m 
     111            v1[1] -= dy * b2m 
     112            v1[2] -= dz * b2m 
     113            v2[0] += dx * b1m 
     114            v2[1] += dy * b1m 
     115            v2[2] += dz * b1m 
     116        for (r, [vx, vy, vz], m) in bodies: 
     117            r[0] += dt * vx 
     118            r[1] += dt * vy 
     119            r[2] += dt * vz 
     120 
     121 
     122def report_energy(bodies=SYSTEM, pairs=PAIRS, e=0.0): 
     123    #print ("Bodies Count:%s\nPairs Count:%s"%(len(bodies),len(pairs))) 
     124    for (((x1, y1, z1), v1, m1), 
     125         ((x2, y2, z2), v2, m2)) in pairs: 
     126        dx = x1 - x2 
     127        dy = y1 - y2 
     128        dz = z1 - z2 
     129        #print (m1*m2) 
     130        e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5) 
     131    for (r, [vx, vy, vz], m) in bodies: 
     132        e += m * (vx * vx + vy * vy + vz * vz) / 2. 
     133    print("%.9f" % e) 
     134 
     135def offset_momentum(ref, bodies=SYSTEM): 
     136    px=0.0 
     137    py=0.0 
     138    pz=0.0 
     139     
     140    for (r, [vx, vy, vz], m) in bodies: 
     141        px -= vx * m 
     142        py -= vy * m 
     143        pz -= vz * m 
     144    (r, v, m) = ref 
     145    v[0] = px / m 
     146    v[1] = py / m 
     147    v[2] = pz / m 
     148 
     149def main(n, ref='sun'): 
     150    from time import clock 
     151    start = clock() 
     152    report_energy() 
     153    offset_momentum(BODIES[ref]) 
     154    report_energy() 
     155    advance(0.01, n) 
     156    report_energy() 
     157    duration = clock() - start 
     158 
     159    print('duration: %s seconds \n\nDone...'%(duration)) 
     160if __name__ == '__main__': 
     161    main(50000) 
     162 
     163 
     164}}} 
     165 
     166---- 
     167 
     168*Initial Cobra Port:* 
     169 
     170{{{ 
     171# The Computer Language Benchmarks Game 
     172# http://shootout.alioth.debian.org/ 
     173# 
     174# originally by Kevin Carson 
     175# modified by Tupteq, Fredrik Johansson, and Daniel Nanz 
     176# modified by Maciej Fijalkowski 
     177# 2to3 
     178#@number float 
     179class Triple 
     180    pro x from var as decimal  
     181    pro y from var as decimal  
     182    pro z from var as decimal 
     183    get squared as decimal 
     184        return .x*.x + .y*.y + .z*.z 
     185     
     186    cue init(x as decimal, y as decimal, z as decimal) 
     187        base.init 
     188        .x = x 
     189        .y = y 
     190        .z = z 
     191         
     192    def toString as String is override 
     193        return "\[[.x], [.y], [.z]\]" 
     194 
     195class Body 
     196    pro coord from var as Triple 
     197    pro velocity from var as Triple 
     198    pro mass from var as decimal 
     199     
     200    cue init(coord as Triple, velocity as Triple, mass as decimal) 
     201        base.init 
     202        .coord = coord 
     203        .velocity = velocity 
     204        .mass = mass   
     205         
     206class BodyPair 
     207    pro body1 from var as Body 
     208    pro body2 from var as Body   
     209     
     210    cue init(body1 as Body, body2 as Body) 
     211        base.init 
     212        .body1 = body1 
     213        .body2 = body2 
     214 
     215class Program 
     216    def combinations(l) as List<of BodyPair> 
     217        result = List<of BodyPair>() 
     218        for x in l.count - 1 
     219            ls = l[x+1:] 
     220            for y in ls 
     221                result.add(BodyPair(l[x],y)) 
     222        return result 
     223 
     224    var bODIES 
     225    cue init 
     226        base.init 
     227     
     228        .bODIES = { 
     229            'sun': Body(Triple(0.0, 0.0, 0.0), Triple(0.0, 0.0, 0.0), .sOLAR_MASS), 
     230 
     231            'jupiter': Body(Triple(4.84143144246472090, 
     232                -1.16032004402742839, 
     233                -0.103622044471123109), 
     234                Triple(0.00166007664274403694 * .dAYS_PER_YEAR, 
     235                0.00769901118419740425 * .dAYS_PER_YEAR, 
     236                -0.0000690460016972063023 * .dAYS_PER_YEAR), 
     237                0.000954791938424326609 * .sOLAR_MASS), 
     238 
     239            'saturn': Body(Triple(8.34336671824457987, 
     240                4.12479856412430479, 
     241                -0.403523417114321381), 
     242                Triple(-0.00276742510726862411 * .dAYS_PER_YEAR, 
     243                0.00499852801234917238 * .dAYS_PER_YEAR, 
     244                0.0000230417297573763929 * .dAYS_PER_YEAR), 
     245                0.000285885980666130812 * .sOLAR_MASS), 
     246 
     247            'uranus': Body(Triple(12.8943695621391310, 
     248                -15.1111514016986312, 
     249                -0.223307578892655734), 
     250                Triple(0.00296460137564761618* .dAYS_PER_YEAR, 
     251                0.00237847173959480950* .dAYS_PER_YEAR, 
     252                -0.0000296589568540237556* .dAYS_PER_YEAR), 
     253                0.0000436624404335156298* .sOLAR_MASS), 
     254 
     255            'neptune': Body(Triple(15.3796971148509165, 
     256                -25.9193146099879641, 
     257                0.179258772950371181), 
     258                Triple(0.00268067772490389322* .dAYS_PER_YEAR, 
     259                0.00162824170038242295* .dAYS_PER_YEAR, 
     260                -0.0000951592254519715870* .dAYS_PER_YEAR), 
     261                0.0000515138902046611451* .sOLAR_MASS)  
     262        } 
     263        .sYSTEM = [.bODIES['sun'],  
     264                    .bODIES['jupiter'], 
     265                    .bODIES['saturn'], 
     266                    .bODIES['uranus'], 
     267                    .bODIES['neptune'] 
     268        ]  
     269        .pAIRS = .combinations(.sYSTEM) 
     270    var sYSTEM  
     271    var _pI = 3.14159265358979323 
     272    var sOLAR_MASS = 4 * 3.14159265358979323 *  3.14159265358979323 
     273    var dAYS_PER_YEAR = 365.24 
     274 
     275    var pAIRS 
     276 
     277 
     278    def advance(dt as decimal, n as int) 
     279        bodies=.sYSTEM 
     280        pairs=.pAIRS 
     281 
     282        for i in n 
     283            for p in pairs 
     284                x1, y1, z1 = p.body1.coord.x, p.body1.coord.y, p.body1.coord.z 
     285                x2, y2, z2 = p.body2.coord.x, p.body2.coord.y, p.body2.coord.z 
     286                m1, m2 = p.body1.mass, p.body2.mass 
     287                v1, v2 =p.body1.velocity, p.body2.velocity 
     288                dx = x1 - x2 
     289                dy = y1 - y2 
     290                dz = z1 - z2 
     291                mag = dt * (Math.pow(Convert.toDouble(dx * dx + dy * dy + dz * dz), -1.5f) to decimal) 
     292                b1m = m1 * mag 
     293                b2m = m2 * mag 
     294                v1.x = v1.x - dx * b2m 
     295                v1.y = v1.y - dy * b2m 
     296                v1.z = v1.z - dz * b2m 
     297                v2.x = v2.x + dx * b1m 
     298                v2.y = v2.y + dy * b1m 
     299                v2.z = v2.z + dz * b1m 
     300            for b in bodies 
     301                r = b.coord 
     302                vx,vy,vz = b.velocity.x, b.velocity.y, b.velocity.z 
     303                r.x = r.x + dt * vx 
     304                r.y = r.y + dt * vy 
     305                r.z = r.z + dt * vz 
     306 
     307 
     308    def report_energy 
     309        bodies=.sYSTEM 
     310        pairs=.pAIRS 
     311        #print "Bodies Count:[bodies.count]\nPairs Count:[pairs.count]" 
     312        e=0.0 
     313        for p in pairs 
     314            m = p.body1.mass * p.body2.mass 
     315            #print m 
     316            dx = p.body1.coord.x - p.body2.coord.x 
     317            dy = p.body1.coord.y - p.body2.coord.y 
     318            dz = p.body1.coord.z - p.body2.coord.z 
     319            sqrs = Convert.toDouble(dx * dx + dy * dy + dz * dz)  
     320            e = e - (m / Math.pow(sqrs , 0.5f) to decimal) 
     321        for b in bodies 
     322            e = e + b.mass * b.velocity.squared / 2.0 
     323        print e 
     324 
     325    def offset_momentum(ref1 ) 
     326        bodies=.sYSTEM 
     327        px=0.0  
     328        py=0.0 
     329        pz=0.0 
     330 
     331        for b in bodies 
     332            vx ,vy ,vz  = b.velocity.x, b.velocity.y,b.velocity.z 
     333            m = b.mass 
     334            px = px - vx * m 
     335            py =py - vy * m 
     336            pz = pz - vz * m 
     337        v, m = ref1.velocity, ref1.mass 
     338        v.x = px / m 
     339        v.y = py / m 
     340        v.z = pz / m 
     341        ref1.velocity = v 
     342        print v 
     343 
     344    def main 
     345        sw = System.Diagnostics.Stopwatch() 
     346        sw.start 
     347        ref1 = 'sun' 
     348        .report_energy 
     349        .offset_momentum(  .bODIES[ref1]) 
     350        .report_energy 
     351        .advance(0.01, 50000) 
     352        .report_energy 
     353        sw.stop 
     354        print 'duration: [sw.elapsedMilliseconds] ms ' 
     355 
     356 
     357}}} 
     358 
     359---- 
     360 
     361*Performance Improved version of Cobra code:* 
     362 
     363 
     364{{{ 
     365# The Computer Language Benchmarks Game 
     366# http://shootout.alioth.debian.org/ 
     367# 
     368# originally by Kevin Carson 
     369# modified by Tupteq, Fredrik Johansson, and Daniel Nanz 
     370# modified by Maciej Fijalkowski 
     371# 2to3 
     372@number float 
     373class Triple 
     374    var x as float  
     375    var y as float  
     376    var z as float 
     377    #pro x from var as float  
     378    #pro y from var as float  
     379    #pro z from var as float 
     380    get squared as float 
     381        return .x*.x + .y*.y + .z*.z 
     382     
     383    cue init(x as float, y as float, z as float) 
     384        base.init 
     385        .x = x 
     386        .y = y 
     387        .z = z 
     388         
     389    def toString as String is override 
     390        return "\[[.x], [.y], [.z]\]" 
     391 
     392class Body 
     393    var coord as Triple 
     394    var velocity as Triple 
     395    var mass as float 
     396    #pro coord from var as Triple 
     397    #pro velocity from var as Triple 
     398    #pro mass from var as float 
     399     
     400    cue init(coord as Triple, velocity as Triple, mass as float) 
     401        base.init 
     402        .coord = coord 
     403        .velocity = velocity 
     404        .mass = mass   
     405 
     406    def updateCoordinates(dt as float) 
     407        .coord.x += dt * .velocity.x 
     408        .coord.y += dt * .velocity.y 
     409        .coord.z += dt * .velocity.z 
     410 
     411         
     412class BodyPair 
     413    var body1 as Body 
     414    var body2 as Body   
     415    #pro body1 from var as Body 
     416    #pro body2 from var as Body   
     417     
     418    cue init(body1 as Body, body2 as Body) 
     419        base.init 
     420        .body1 = body1 
     421        .body2 = body2 
     422 
     423class Program 
     424    def combinations(l) as List<of BodyPair> 
     425        result = List<of BodyPair>() 
     426        for x in l.count - 1 
     427            ls = l[x+1:] 
     428            for y in ls 
     429                result.add(BodyPair(l[x],y)) 
     430        return result 
     431 
     432    cue init 
     433        base.init 
     434     
     435        bodies as Dictionary<of String, Body> = { 
     436            'sun': Body(Triple(0.0, 0.0, 0.0), Triple(0.0, 0.0, 0.0), .sOLAR_MASS), 
     437 
     438            'jupiter': Body(Triple(4.84143144246472090, 
     439                -1.16032004402742839, 
     440                -0.103622044471123109), 
     441                Triple(0.00166007664274403694 * .dAYS_PER_YEAR, 
     442                0.00769901118419740425 * .dAYS_PER_YEAR, 
     443                -0.0000690460016972063023 * .dAYS_PER_YEAR), 
     444                0.000954791938424326609 * .sOLAR_MASS), 
     445 
     446            'saturn': Body(Triple(8.34336671824457987, 
     447                4.12479856412430479, 
     448                -0.403523417114321381), 
     449                Triple(-0.00276742510726862411 * .dAYS_PER_YEAR, 
     450                0.00499852801234917238 * .dAYS_PER_YEAR, 
     451                0.0000230417297573763929 * .dAYS_PER_YEAR), 
     452                0.000285885980666130812 * .sOLAR_MASS), 
     453 
     454            'uranus': Body(Triple(12.8943695621391310, 
     455                -15.1111514016986312, 
     456                -0.223307578892655734), 
     457                Triple(0.00296460137564761618* .dAYS_PER_YEAR, 
     458                0.00237847173959480950* .dAYS_PER_YEAR, 
     459                -0.0000296589568540237556* .dAYS_PER_YEAR), 
     460                0.0000436624404335156298* .sOLAR_MASS), 
     461 
     462            'neptune': Body(Triple(15.3796971148509165, 
     463                -25.9193146099879641, 
     464                0.179258772950371181), 
     465                Triple(0.00268067772490389322* .dAYS_PER_YEAR, 
     466                0.00162824170038242295* .dAYS_PER_YEAR, 
     467                -0.0000951592254519715870* .dAYS_PER_YEAR), 
     468                0.0000515138902046611451* .sOLAR_MASS)  
     469        } 
     470        .sYSTEM = List<of Body>() 
     471        .sYSTEM.add(bodies['sun']) 
     472        .sYSTEM.add(bodies['jupiter']) 
     473        .sYSTEM.add(bodies['saturn']) 
     474        .sYSTEM.add(bodies['uranus']) 
     475        .sYSTEM.add(bodies['neptune']) 
     476        .pAIRS = .combinations(.sYSTEM) 
     477    var sYSTEM as List<of Body> 
     478    var sOLAR_MASS as float = 4 * 3.14159265358979323 *  3.14159265358979323 
     479    var dAYS_PER_YEAR as float = 365.24 
     480 
     481    var pAIRS as List<of BodyPair> 
     482 
     483 
     484    def advance(dt as float, n as int) 
     485        bodies=.sYSTEM 
     486        pairs=.pAIRS 
     487        b1 as Body = bodies[0] 
     488        b2 as Body = bodies[0] 
     489        v1 as Triple = b1.velocity 
     490        v2 as Triple = b2.velocity 
     491        dx as float 
     492        dy as float 
     493        dz as float 
     494        mag as float 
     495 
     496        for i in n 
     497            for p as BodyPair in pairs 
     498                b1 = p.body1 
     499                b2 = p.body2 
     500                v1, v2 =b1.velocity, b2.velocity 
     501                dx = b1.coord.x - b2.coord.x 
     502                dy = b1.coord.y - b2.coord.y 
     503                dz = b1.coord.z - b2.coord.z 
     504                mag = dt * (Math.pow(dx * dx + dy * dy + dz * dz, -1.5f) ) 
     505                b1m as float= b1.mass * mag 
     506                b2m as float = b2.mass * mag 
     507                v1.x = v1.x - dx * b2m 
     508                v1.y = v1.y - dy * b2m 
     509                v1.z = v1.z - dz * b2m 
     510                v2.x = v2.x + dx * b1m 
     511                v2.y = v2.y + dy * b1m 
     512                v2.z = v2.z + dz * b1m 
     513            for b as Body in bodies 
     514                b.updateCoordinates(dt) 
     515 
     516 
     517    def report_energy 
     518        bodies=.sYSTEM 
     519        pairs=.pAIRS 
     520        #print "Bodies Count:[bodies.count]\nPairs Count:[pairs.count]" 
     521        e as float=0.0 
     522        for p as BodyPair in pairs 
     523            b1 as Body = p.body1 
     524            b2 as Body = p.body2 
     525            m = b1.mass * b2.mass 
     526            #print m 
     527            dx = b1.coord.x - b2.coord.x 
     528            dy = b1.coord.y - b2.coord.y 
     529            dz = b1.coord.z - b2.coord.z 
     530            sqrs = dx * dx + dy * dy + dz * dz  
     531            e -= m / Math.sqrt(sqrs )  
     532        for b as Body in bodies 
     533            e += b.mass * b.velocity.squared *0.5 
     534        print e 
     535 
     536    def offset_momentum(ref1 as Body) 
     537        bodies=.sYSTEM 
     538        px as float=0.0  
     539        py as float=0.0 
     540        pz as float=0.0 
     541 
     542        for b as Body in bodies 
     543            m as float = b.mass 
     544            px = px- b.velocity.x * m 
     545            py = py-b.velocity.y * m 
     546            pz = pz-b.velocity.z * m 
     547        v, m = ref1.velocity, 1/ ref1.mass 
     548        v.x = px * m 
     549        v.y = py * m 
     550        v.z = pz * m 
     551        print v 
     552 
     553    def main 
     554        sw = System.Diagnostics.Stopwatch() 
     555        sw.start 
     556        .report_energy 
     557        .offset_momentum( .sYSTEM[0]) #sun 
     558        .report_energy 
     559        .advance(0.01, 50000) 
     560        .report_energy 
     561        sw.stop 
     562        print 'duration: [sw.elapsed] ms ' 
     563 
     564 
     565}}}