tricu

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

float_chains.py (9340B)


      1 #!/usr/bin/env python3
      2 """Chained operations: checked words, trusted words, operation-local and pooled caches."""
      3 import argparse
      4 import hashlib
      5 import json
      6 from pathlib import Path
      7 import random
      8 import statistics
      9 
     10 from float_aligned_quantize import nearest
     11 from float_quantize import ROOT, dyadic, value
     12 from float_multiply import Runner, sha
     13 from base_workloads import encode as nat
     14 from http_headers import LEAF, TRUE, pair, tree_list, expect
     15 from float_finite import SIGN, LIMIT, MASK, finite_value, cases as add_cases
     16 from float_finite_product_quotient import cases as product_cases
     17 
     18 from float_ieee import OPS, oracle, encoded, class_words, SIGN, INF, QNAN, MIN_NORMAL
     19 
     20 VARIANTS=['checkedBatch','trustedBatch','localBatch','freshBatch','cachedBatch']
     21 
     22 
     23 def chain_oracle(row):
     24     value,pool,steps=row
     25     flags=[False]*5
     26     for op,index in steps:
     27         ok,(value,current)=oracle(value,pool[index],OPS[op]);assert ok
     28         flags=[a or b for a,b in zip(flags,current)]
     29     return value,tuple(flags)
     30 
     31 
     32 def payload(result):
     33     word,flags=result
     34     out=TRUE if flags[-1] else LEAF
     35     for flag in reversed(flags[:-1]):out=pair(TRUE if flag else LEAF,out)
     36     return pair(nat(word),out)
     37 
     38 
     39 def raw_rows(rows):
     40     return tree_list([pair(nat(initial),pair(tree_list([nat(w) for w in pool]),
     41         tree_list([pair(nat(op),nat(index)) for op,index in steps]))) for initial,pool,steps in rows])
     42 
     43 
     44 def cases():
     45     words=class_words()
     46     rows=[(a,[b],[(op,0)]) for a in words for b in words for op in range(4)]
     47     rng=random.Random(751)
     48     for _ in range(96):
     49         pool=[rng.choice(words),rng.getrandbits(64),rng.getrandbits(64)]
     50         rows.append((rng.getrandbits(64),pool,[(rng.randrange(4),rng.randrange(3)) for _ in range(rng.choice([0,1,4,16]))]))
     51     rows += [
     52         (MIN_NORMAL,[0x3fefffffffffffff,0x4000000000000000],[(2,0),(2,1)]),
     53         (1,[0x3fe0000000000000,INF],[(2,0),(2,1)]),
     54         (0x7fefffffffffffff,[0x4000000000000000,0,INF],[(2,0),(2,1),(0,2)]),
     55         (0x3ff0000000000000,[0,INF],[(3,0),(3,1)]),
     56         (QNAN|1,[INF|1,SIGN|QNAN|2],[(0,0),(1,1)]),
     57         (0x3ff0000000000000,[0x3ca0000000000000],[(0,0),(0,0)])]
     58     for op in range(4):
     59         for n in [0,1,4,16,64]:rows.append((0x3ff0000000000000,[0x3ff0000000000001],[(op,0)]*n))
     60     # Observe every prefix, and each primitive step with fresh clear flags.
     61     # This makes each step's word and fresh flags independently observable.
     62     expanded=list(rows)
     63     for initial,pool,steps in rows:
     64         for stop in range(1,len(steps)):
     65             expanded.append((initial,pool,steps[:stop]))
     66         word=initial
     67         for op,index in steps:
     68             expanded.append((word,[pool[index]],[(op,0)]))
     69             ok,(word,flags)=oracle(word,pool[index],OPS[op]);assert ok
     70     unique={}
     71     for initial,pool,steps in expanded:
     72         unique.setdefault((initial,tuple(pool),tuple(steps)),(initial,pool,steps))
     73     return list(unique.values())
     74 
     75 
     76 def groups():
     77     result={}
     78     for n in [1,4,16]:
     79         result[f'add-reuse-{n}']=[(0x3ff0000000000000+i,[0x3ca0000000000001],[(0,0)]*n) for i in range(4)]
     80         result[f'affine-reuse-{n}']=[(0x3ff0000000000000+i,[0x3fefffffffffffff,0x3ca0000000000001],[(2,0),(0,1)]*n) for i in range(4)]
     81         result[f'divide-reuse-{n}']=[(0x3ff0000000000000+i,[0x3ff8123456789abc],[(3,0)]*n) for i in range(4)]
     82     result['affine-unique-16']=[(0x3ff0000000000000+i,
     83         [w for _ in range(16) for w in [0x3fefffffffffffff,0x3ca0000000000001]],
     84         [(0 if j%2 else 2,j) for j in range(32)]) for i in range(4)]
     85     # Equal-valued separate pool slots and varying operand values.
     86     result['add-varying-16']=[(0x3ff0000000000000+i,[0x3ca0000000000001+j for j in range(16)],[(0,j) for j in range(16)]) for i in range(4)]
     87     result['exceptional-16']=[(1,[0x3fe0000000000000,INF],[(2,0),(2,1)]*8) for _ in range(4)]
     88     return result
     89 
     90 
     91 def main():
     92     p=argparse.ArgumentParser(description=__doc__)
     93     p.add_argument('tricu',type=Path);p.add_argument('--directory',required=True,type=Path)
     94     args=p.parse_args();root=args.directory.resolve();root.mkdir(parents=True,exist_ok=False)
     95     work=root/'workspace';work.mkdir()
     96     sources={'driver.tri':'bench/float-chains.tri','ieee.tri':'bench/float-ieee.tri','product_quotient.tri':'bench/float-finite-product-quotient.tri',
     97              'finite.tri':'bench/float-finite.tri','adders.tri':'bench/float-adders.tri',
     98              'multiply.tri':'bench/float-multiply.tri','near.tri':'bench/float-near-jam.tri',
     99              'aligned.tri':'bench/float-aligned-quantize.tri','fast.tri':'bench/float-quantize-fast.tri',
    100              'quantize.tri':'bench/float-quantize.tri','pipelines.tri':'bench/float-pipelines.tri',
    101              'kernels.tri':'bench/float-significands.tri','layouts.tri':'bench/float-layouts.tri',
    102              'native_math.tri':'lib/math/native.tri'}
    103     for name,path in sources.items():(work/name).write_bytes((ROOT/path).read_bytes())
    104     report=dict(complete=False,scope='chained RNE operations; valid word inputs; construction charged; no public representation',
    105                 runtime_sha256=sha(args.tricu),compiler_sha256=sha(ROOT/'bootstrap/build-stable.arboricx'),
    106                 harness_sha256=sha(Path(__file__)),sources={n:sha(ROOT/p) for n,p in sources.items()},
    107                 validation=[],demand=[],samples=[],summaries=[])
    108     runner=Runner(args.tricu,root,2400,memory_mib=2048)
    109     try:
    110         tool=runner.imported(ROOT/'bootstrap/build-stable.arboricx','compiler');bundle=root/'ieee.arboricx'
    111         runner.run('compile',['compile','--file',work/'driver.tri','--toolchain',tool,'--tool-store',runner.store,
    112                              '--store',runner.store,'--output',bundle,'--compile-timeout','240'],270)
    113         pin=runner.imported(bundle,'ieee');report['bundle_sha256']=sha(bundle)
    114         def run(label,name,raw,wanted,count,measure=False):
    115             file=root/(label+'.input.tree');file.write_bytes(raw)
    116             actual=runner.invoke(label,pin,name,['--arg-tree-file',file]+(['--profile-full-result'] if measure else []))
    117             expect(actual,wanted,label)
    118             if not measure:
    119                 report['validation'].append(dict(label=label,cases=count,input_sha256=sha(file),
    120                     output_sha256=hashlib.sha256(actual).hexdigest()));return
    121             points={v['profile']:v for line in (root/(label+'.log')).read_text().splitlines()
    122                     if line.startswith('{') for v in [json.loads(line)]}
    123             a,b=points['invoke-begin-1'],points['force-end-1']
    124             return dict(wall_ns=b['wall_ns']-a['wall_ns'],allocated_bytes=b['allocated_bytes']-a['allocated_bytes'])
    125         def material(rows):
    126             return raw_rows(rows),tree_list([payload(chain_oracle(row)) for row in rows])
    127         rows=cases();report['chain_cases']=len(rows)
    128         for name in VARIANTS:
    129             for offset in range(0,len(rows),12):
    130                 batch=rows[offset:offset+12];raw,wanted=material(batch)
    131                 run(f'check-{name}-{offset}',name,raw,wanted,len(batch))
    132         def flag_tree(mask):
    133             bits=[bool(mask&(1<<i)) for i in range(5)]
    134             out=TRUE if bits[-1] else LEAF
    135             for bit in reversed(bits[:-1]):out=pair(TRUE if bit else LEAF,out)
    136             return out
    137         flag_pairs=[(a,b) for a in range(32) for b in range(32)]
    138         for offset in range(0,len(flag_pairs),32):
    139             batch=flag_pairs[offset:offset+32]
    140             run('flags-'+str(offset),'mergeFlags',tree_list([pair(flag_tree(a),flag_tree(b)) for a,b in batch]),
    141                 tree_list([flag_tree(a|b) for a,b in batch]),len(batch))
    142         actual=runner.invoke('demand-flags',pin,'flagDemand',['--arg','0'])
    143         expect(actual,TRUE,'flagDemand');report['demand'].append(dict(name='flagDemand',outcome='expected-result'))
    144         for name,word in [('emptyChecked',SIGN),('emptyCached',SIGN),('unusedPool',0x3ff0000000000000)]:
    145             actual=runner.invoke('demand-'+name,pin,name,['--arg','0'])
    146             expect(actual,payload((word,(False,)*5)),name)
    147             report['demand'].append(dict(name=name,outcome='expected-result'))
    148         rng=random.Random(751)
    149         jobs=[]
    150         for group,batch in groups().items():
    151             for name in VARIANTS:
    152                 raw,wanted=material(batch);jobs.append((group,name,raw,wanted,len(batch)))
    153         for rep in range(5):
    154             schedule=list(jobs);rng.shuffle(schedule)
    155             for group,name,raw,wanted,size in schedule:
    156                 sample=run(f'measure-{group}-{name}-{rep}',name,raw,wanted,size,True)
    157                 report['samples'].append(dict(group=group,name=name,size=size,repeat=rep,warmup=rep==0,**sample))
    158         for group,name,_,_,size in jobs:
    159             samples=[s for s in report['samples'] if s['group']==group and s['name']==name and not s['warmup']]
    160             row=dict(group=group,name=name,size=size)
    161             for key in ['wall_ns','allocated_bytes']:
    162                 row[key]=statistics.median(s[key] for s in samples)
    163                 row[key+'_min']=min(s[key] for s in samples);row[key+'_max']=max(s[key] for s in samples)
    164             report['summaries'].append(row);print(json.dumps(row),flush=True)
    165         report['complete']=True
    166     finally:(root/'report.json').write_text(json.dumps(report,indent=2)+'\n')
    167 
    168 
    169 if __name__=='__main__':main()