tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

binding_compiler_small.py (7134B)


      1 #!/usr/bin/env python3
      2 """Bounded standalone integration/measurement after the cold-workspace timeout."""
      3 import argparse,json,random,statistics,time
      4 from pathlib import Path
      5 from binding_substitution import ROOT,HELPERS
      6 from abstraction_binding_model import programs,source
      7 from float_multiply import Runner,sha
      8 from bundle_dag import Intern,read
      9 from base_workloads import encode as nat
     10 from http_headers import LEAF,TRUE,pair,tree_list,expect
     11 
     12 
     13 def main():
     14     p=argparse.ArgumentParser(description=__doc__)
     15     p.add_argument('tricu',type=Path);p.add_argument('--candidate',type=Path,required=True)
     16     p.add_argument('--directory',type=Path,required=True);args=p.parse_args()
     17     candidate=args.candidate.resolve();prior=json.loads((candidate/'report.json').read_text())
     18     assert prior['complete'] and sha(candidate/'candidate.arboricx')==prior['candidate_sha256']
     19     root=args.directory.resolve();root.mkdir(parents=True,exist_ok=False)
     20     driver=root/'driver.tri'
     21     driver.write_text(HELPERS+'\n'+'\n'.join(name+' = '+source(expr) for name,expr in programs().items())+'''
     22 map f xs = triage t (_ : t) (a b : t (f a) (map f b)) xs
     23 branches xs = map (row : branchBound (head row) (tail row)) xs
     24 adders xs = map (row : adderBound (head row) (head (tail row)) (tail (tail row))) xs
     25 copies xs = map sharedBound xs
     26 partialBranch _ = branchNegatedHead branchBound
     27 partialCarryZero _ = adderKnownCarryZero adderBound
     28 partialCarryOne _ = adderKnownCarryOne adderBound
     29 ''')
     30     runner=Runner(args.tricu,root,480,memory_mib=2048)
     31     report=dict(complete=False,scope='standalone-module-not-cold-arithmetic-workspace',
     32                 runtime_sha256=sha(args.tricu),candidate_report_sha256=sha(candidate/'report.json'),
     33                 harness_sha256=sha(Path(__file__)),source_sha256=sha(driver),compilation=[],samples=[],checks=[])
     34     try:
     35         tools={role:runner.imported(path,role) for role,path in [('accepted',ROOT/'bootstrap/build-stable.arboricx'),('candidate',candidate/'candidate.arboricx')]}
     36         bundles={};rng=random.Random(5150)
     37         for repeat in range(5):
     38             order=list(tools);rng.shuffle(order)
     39             for role in order:
     40                 label=f'compile-{repeat}-{role}';out=root/(label+'.arboricx')
     41                 start=time.monotonic()
     42                 runner.run(label,['compile','--file',driver,'--toolchain',tools[role],'--tool-store',runner.store,
     43                            '--store',root/(label+'-store'),'--output',out,'--compile-timeout','45'],60)
     44                 report['compilation'].append(dict(role=role,repeat=repeat,wall_seconds=time.monotonic()-start,
     45                                                   rts=runner.records[-1]['rts_process']))
     46                 if role in bundles:assert bundles[role].read_bytes()==out.read_bytes()
     47                 bundles[role]=out
     48         intern=Intern();graphs={};pins={}
     49         for role,bundle in bundles.items():
     50             _,_,nodes,exports,_=read(bundle);ids=intern.graph(nodes)
     51             graphs[role]={name:ids[index] for name,index in exports.items()}
     52             pins[role]=runner.imported(bundle,'import-'+role)
     53         old,new=graphs['accepted'],graphs['candidate']
     54         report['changed_controls']=[]
     55         for name in programs():
     56             if name in ['singleBound','branchBound','adderBound']:
     57                 assert new[name]==old[name.replace('Bound','Inline')]
     58                 report['changed_controls'].append(name)
     59             else:assert new[name]==old[name],name
     60         report['unchanged_controls']=15
     61         def invoke(role,name,raw,wanted,label,measure=False):
     62             path=root/(label+'.input.tree');path.write_bytes(raw)
     63             actual=runner.invoke(label,pins[role],name,['--arg-tree-file',path]+(['--profile-full-result'] if measure else []))
     64             expect(actual,wanted,label)
     65             if not measure:report['checks'].append(dict(role=role,name=name));return
     66             points={r['profile']:r for line in (root/(label+'.log')).read_text().splitlines()
     67                     if line.startswith('{') for r in [json.loads(line)]}
     68             a,b=points['invoke-begin-1'],points['force-end-1']
     69             return dict(wall_ns=b['wall_ns']-a['wall_ns'],allocated_bytes=b['allocated_bytes']-a['allocated_bytes'])
     70         for role in tools:
     71             for name,wanted in [('partialBranch',LEAF),('partialCarryZero',LEAF),('partialCarryOne',TRUE),('unusedBound',LEAF)]:
     72                 invoke(role,name,LEAF,wanted,'partial-'+role+name)
     73             for name,arg in [('closureBound',LEAF),('closedBranchBound',TRUE)]:
     74                 try:invoke(role,name,arg,LEAF,'demand-'+role+name);raise AssertionError('expected timeout')
     75                 except RuntimeError as error:
     76                     if str(error)!='wall-time guard exceeded':raise
     77                     report['checks'].append(dict(role=role,name=name,outcome='expected-timeout'))
     78         flags=[(bool(i&1),bool(i&2)) for i in range(1024)]
     79         boolean=lambda b:TRUE if b else LEAF
     80         rows=[(bool(i&1),bool(i&2),bool(i&4)) for i in range(1024)]
     81         xs=list(range(128));copied=tree_list([b'\1'+nat(x) for x in xs])
     82         workloads={
     83             'branches':(tree_list([pair(boolean(a),boolean(b)) for a,b in flags]),
     84                         tree_list([pair(boolean(not b),boolean(b)) if a else pair(boolean(b),boolean(not b)) for a,b in flags])),
     85             'adders':(tree_list([pair(boolean(a),pair(boolean(b),boolean(c))) for a,b,c in rows]),
     86                       tree_list([pair(boolean((a+b+c)%2),boolean(a+b+c>=2)) for a,b,c in rows])),
     87             'copies':(nat([xs]*4),tree_list([pair(copied,copied)]*4))}
     88         for repeat in range(5):
     89             jobs=[(role,name) for role in tools for name in workloads];rng.shuffle(jobs)
     90             for role,name in jobs:
     91                 raw,wanted=workloads[name]
     92                 values=invoke(role,name,raw,wanted,f'time-{repeat}-{role}-{name}',True)
     93                 report['samples'].append(dict(role=role,name=name,repeat=repeat,**values))
     94         report['runtime']=[]
     95         for name in workloads:
     96             values={role:{key:statistics.median(r[key] for r in report['samples'] if r['role']==role and r['name']==name and r['repeat']>0)
     97                           for key in ['wall_ns','allocated_bytes']} for role in tools}
     98             report['runtime'].append(dict(name=name,medians=values,ratios={k:values['candidate'][k]/values['accepted'][k] for k in values['accepted']}))
     99         values={role:dict(wall_seconds=statistics.median(r['wall_seconds'] for r in report['compilation'] if r['role']==role and r['repeat']>0),
    100                          allocated_bytes=statistics.median(r['rts']['allocated_bytes'] for r in report['compilation'] if r['role']==role and r['repeat']>0)) for role in tools}
    101         report['compile_summary']=dict(medians=values,ratios={k:values['candidate'][k]/values['accepted'][k] for k in values['accepted']})
    102         report['complete']=True;print(json.dumps(dict(compile=report['compile_summary'],runtime=report['runtime']),indent=2))
    103     finally:(root/'report.json').write_text(json.dumps(report,indent=2)+'\n')
    104 
    105 
    106 if __name__=='__main__':main()