module: dispatch-profiler Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define method function-number-required (f :: ) => (res :: ) size(function-specializers(f)) end method; define constant = false-or(); define function as-hit-count (x :: ) => (res :: ) if (as(, %profile-count-high(x)) = 0) as(, %profile-count-low(x)) else make(, low: %profile-count-low(x), high: %profile-count-high(x)) end if end function; define macro incf { incf(?x:expression, ?i:expression) } => { ?x := generic/+(?x, ?i) } end macro; define constant $max-number-parameters = 256; define constant $shared-argument-types :: = make(, size: $max-number-parameters, fill: ); define sealed class () slot dws-generic :: ; slot dws-id :: false-or() = #f; slot dws-library :: false-or() = #f; slot dws-size :: = 0; constant slot dws-method-weights :: = make(
); // slot dws-top-profile :: false-or(); slot dws-arg-types :: = $shared-argument-types; slot dws-partial-types :: = $shared-argument-types; slot dws-cache-hits :: = 0; slot dws-cache-attempts :: = 0; end class; define sealed class () slot dmw-hits :: = 0; slot dmw-weighted-hits :: = 0; end class; define function walk-all-libraries (top-lib :: , walk-library :: ) let walked = make(
); local method walk-all-libraries*(lib :: , walked :: ) unless (element(walked, lib, default: #f)) walk-library(lib); element(walked, lib) := #t; for (used-library* in used-libraries(lib)) walk-all-libraries*(used-library(used-library*), walked) end for; end unless; end method; walk-all-libraries*(top-lib, make(
)) end function; define function dispatch-walk-all-generics (top-lib :: , f :: , generics-walked :: ) walk-all-libraries (top-lib, rcurry(dispatch-walk-library-generics, f, generics-walked)); end function; define function dispatch-walk-library-generics (lib :: , f :: , generics-walked :: ) for (g :: in library-defined-generics(lib)) unless (element(generics-walked, g, default: #f)) element(generics-walked, g) := #t; f(g) end unless; end for; end function; // define function dispatch-walk-all-generic-trees // (top-lib :: , f :: , // generics-walked :: , caches-walked :: ) // dispatch-walk-all-generics // (top-lib, rcurry(dispatch-walk-generic-tree, f, caches-walked), generics-walked) // end function; define function make-type-vector (size :: ) => (res :: ) make(, size: size, fill: ); end function; // define function dispatch-walk-generic-tree // (g :: , f :: , caches-walked :: ) // let dws :: = make(); // dws-arg-types(dws) := make-type-vector(function-number-required(g)); // dws-partial-types(dws) := make-type-vector(function-number-required(g)); // dws-generic(dws) := g; // let tree = discriminator(g); // dws-id(dws) := -1; // dws-library(dws) := #f; // dws-size(dws) := cache-info-size(g); // remove-all-keys!(dws-method-weights(dws)); // dispatch-walker(dws, tree, identity, 0, 0); // f(dws); // end function; define function dispatch-walk-all-call-sites (top-lib :: , f :: , generics-walked :: , caches-walked :: ) walk-all-libraries (top-lib, rcurry(dispatch-walk-library-call-sites, f, generics-walked, caches-walked)); end function; define function dispatch-walk-library-call-sites (lib :: , f :: , generics-walked :: , caches-walked :: ) let dws :: = make(); for (g :: in library-defined-generics(lib)) unless (element(generics-walked, g, default: #f)) dws-arg-types(dws) := make-type-vector(function-number-required(g)); dws-partial-types(dws) := make-type-vector(function-number-required(g)); dws-generic(dws) := g; let tree = discriminator(g); dws-id(dws) := -1; dws-library(dws) := #f; dws-size(dws) := cache-info-size(g); dws-cache-attempts(dws) := 0; dws-cache-hits(dws) := 0; element(caches-walked, tree) := #t; remove-all-keys!(dws-method-weights(dws)); dispatch-walker(dws, tree, identity, 0, 0); f(dws); let cache = %gf-cache(g); when (instance?(cache, )) for (user in gf-cache-info-users(cache)) when (user & ~element(caches-walked, cache-header-engine-node-next(user), default: #f)) element(caches-walked, user) := #t; remove-all-keys!(dws-method-weights(dws)); let parent = cache-header-engine-node-parent(user); let profiling-parent? = instance?(parent, ); when (profile-all-terminal-engine-nodes?() | profiling-parent?) let (id, library) = if (profiling-parent?) values(profiling-call-site-cache-header-engine-node-id(parent), profiling-call-site-cache-header-engine-node-library(parent)) else values(-1, #f) // shared end if; dws-id(dws) := id; dws-library(dws) := library; dws-size(dws) := 0; dws-cache-attempts(dws) := 0; dws-cache-hits(dws) := 0; dispatch-walker(dws, user, identity, 0, 0); f(dws); end when; end when; end for; end when; end unless; end for end function; define function dispatch-walk-all-engine-nodes (top-lib :: , f :: , generics-walked :: , caches-walked :: ) walk-all-libraries (top-lib, rcurry(dispatch-walk-library-engine-nodes, f, generics-walked, caches-walked)); end function; define method clear-dispatch-profiling-counters (e) select (e by instance?) => let e :: = e; %profile-count-low(e) := as(, 0); %profile-count-high(e) := as(, 0); => let e :: = e; lckd-hits(e) := 0; => let e :: = e; lsd-hits(e) := 0; otherwise => #f; end select; end method; define function dispatch-walk-library-engine-nodes (lib :: , f :: , generics-walked :: , caches-walked :: ) let dws :: = make(); for (g :: in library-defined-generics(lib)) unless (element(generics-walked, g, default: #f)) let tree = discriminator(g); caches-walked[tree] := #t; dws-generic(dws) := g; dispatch-walker(dws, tree, f, 0, 0); let cache = %gf-cache(g); when (instance?(cache, )) for (user in gf-cache-info-users(cache)) when (user) remove-all-keys!(dws-method-weights(dws)); let cached-root = cache-header-engine-node-next(user); unless (element(caches-walked, cached-root, default: #f)) caches-walked[cached-root] := #t; dispatch-walker(dws, user, f, 0, 0); end unless; end when; end for; end when; end unless; end for; end function; define generic dispatch-walker (dws :: , object, f :: , cost :: , hits :: ) => (hits :: ); define macro with-dispatch-walking-type { with-dispatch-walking-type (?dws:expression, ?type:expression, ?argnum:expression) ?:body end } => { let _dws_ = ?dws; let _type_ = ?type; let _argnum_ = ?argnum; let _otype_ = dws-arg-types(_dws_)[_argnum_]; block () dws-arg-types(_dws_)[_argnum_] := _type_; ?body; afterwards dws-arg-types(_dws_)[_argnum_] := _otype_; end block } end macro; define macro with-preserved-dispatch-walking-types { with-preserved-dispatch-walking-types (?dws:expression) ?body:body end } => { let _dws_ = ?dws; let _otypes_ = dws-arg-types(_dws_); let _ntypes_ = copy-sequence(_otypes_); block () dws-arg-types(_dws_) := _ntypes_; ?body; afterwards dws-arg-types(_dws_) := _otypes_; end block } end macro; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); 0 end method; define method dispatch-walker (dws :: , m :: , f :: , cost :: , hits :: ) => (hits :: ) let mwt ::
= dws-method-weights(dws); let mw :: = element(mwt, m, default: #f) | (mwt[m] := make()); if (hits) let hits :: = hits; dmw-hits(mw) := generic/+(dmw-hits(mw), hits); dmw-weighted-hits(mw) := generic/+(dmw-weighted-hits(mw), generic/*(hits, cost)); hits else 0 end if; end method; define function instance-size (e) => (res :: ) let class = object-class(e); let req-size = size(slot-descriptors(class)) + 1; let rep-slotd = repeated-slot-descriptor(class); if (rep-slotd) let size-slotd = size-slot-descriptor(rep-slotd); let get-size = slot-getter(size-slotd); req-size + get-size(e) else req-size end if end function; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); let type = dws-arg-types(dws)[if (instance?(e, )) 0 else 1 end]; let ic :: = select (type by instance?) => object-implementation-class(singleton-object(type)); => class-implementation-class(type); => type; end select; // dws-size(dws) := dws-size(dws) + instance-size(e); // SHARED let cic :: = iterate find-any-concrete-subclass (ic :: = ic) if (class-abstract?(ic)) block (return) for (sc in direct-subclasses(ic)) let csc = find-any-concrete-subclass(class-implementation-class(sc)); csc & return(csc); end for; #f end block; else ic end if end iterate; let offset :: = callback-slot-engine-node-offset(e); let sd :: false-or() = select (e by instance?) => repeated-slot-descriptor(cic); => instance-slot-descriptors(cic)[offset]; => tail(class-slot-storage(cic)[offset]); end select; let methods = generic-function-methods(dws-generic(dws)); let methood-index = find-key(methods, method (m :: ) instance?(m, ) & sd == method-slot-descriptor(m) end method); if (methood-index) dispatch-walker(dws, methods[methood-index], f, cost, hits) else 0 end if end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dws-size(dws) := dws-size(dws) + instance-size(e); // method and data slots dispatch-walker(dws, single-method-engine-node-method(e), f, cost, hits) end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dws-size(dws) := dws-size(dws) + instance-size(e); // type and next slots dispatch-walker(dws, typecheck-discriminator-next(e), f, cost + 1, hits) end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dws-size(dws) := dws-size(dws) + instance-size(e); // type, then, and else let ncost :: = cost + 1; let th = if-type-discriminator-then(e); let el = if-type-discriminator-else(e); let my-hits :: = 0; with-dispatch-walking-type (dws, if-type-discriminator-type(e), discriminator-argnum(e)) incf(my-hits, dispatch-walker(dws, th, f, ncost, #f)); end; incf(my-hits, dispatch-walker(dws, el, f, ncost, #f)); incf(dws-cache-attempts(dws), my-hits); incf(dws-cache-hits(dws), my-hits); my-hits end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dws-size(dws) := dws-size(dws) + instance-size(e); if (instance?(e, )) let k = monomorphic-by-class-discriminator-key(e); with-dispatch-walking-type (dws, implementation-class-from-key(k), discriminator-argnum(e)) let x = monomorphic-by-class-discriminator-next(e); let my-hits = dispatch-walker(dws, x, f, cost + 1, #f); incf(dws-cache-attempts(dws), my-hits); incf(dws-cache-hits(dws), my-hits); my-hits end else let ncost :: = cost + 1; let my-hits :: = 0; dws-size(dws) := dws-size(dws) + ckd-size(e) + 1; // inlined vector for (i from 0 below ckd-size(e) by 2) let k = ckd-ref(e, i); if (k ~== $ckd-empty) with-dispatch-walking-type (dws, implementation-class-from-key(k), discriminator-argnum(e)) let x = ckd-ref(e, i + 1); incf(my-hits, dispatch-walker(dws, x, f, ncost, #f)) end end if end for; incf(dws-cache-attempts(dws), my-hits); incf(dws-cache-hits(dws), if (instance?(e, )) lckd-hits(e) else 0 end if); my-hits end if end method; // define method dispatch-walker (dws :: , e :: , // cost :: , hits :: ) // => () // dws-size(dws) := dws-size(dws) + 1; // let k = monomorphic-by-class-discriminator-key(e); // with-dispatch-walking-type (dws, implementation-class-from-key(k), discriminator-argnum(e)) // let my-hits = dispatch-walker(dws, x, f, cost + 1, #f); // incf(dws-cache-attempts(dws), my-hits); // incf(dws-cache-hits(dws), my-hits); // my-hits // end // end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dws-size(dws) := dws-size(dws) + instance-size(e) + ckd-size(e) + 1; let ncost :: = cost + 1; let my-hits :: = 0; for (i from 0 below ckd-size(e) by 2) let k = ckd-ref(e, i); if (k ~== $ckd-empty) with-dispatch-walking-type (dws, object-implementation-class(implementation-class-from-key(k)), discriminator-argnum(e)) incf(my-hits, dispatch-walker(dws, ckd-ref(e, i + 1), f, ncost, #f)) end end if end for; incf(my-hits, dispatch-walker(dws, class-keyed-discriminator-default(e), f, cost + 1, #f)); incf(dws-cache-attempts(dws), my-hits); incf(dws-cache-hits(dws), if (instance?(e, )) lckd-hits(e) else 0 end if); my-hits end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); let v :: = singleton-discriminator-table(e); let ncost :: = cost + 1; let my-hits :: = 0; dws-size(dws) := dws-size(dws) + instance-size(e) + size(v) + 1; for (i from 0 below size(v) by 2) let k = v[i]; unless (k == $absent-engine-node) // yeah, that's what goes in the key slot (bad idea though). incf(my-hits, dispatch-walker(dws, v[i + 1], f, ncost, #f)) end unless end for; incf(dws-cache-attempts(dws), my-hits); incf(dws-cache-hits(dws), if (instance?(e, )) lsd-hits(e) else 0 end if); my-hits end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dispatch-walker(dws, cache-header-engine-node-next(e), f, cost, as-hit-count(e)) end method; define method cache-info-size (g :: ) => (res :: ) if (call-site-caches-enabled?()) let cache-info = %gf-cache(g); if (instance?(cache-info, )) instance-size(cache-info) + instance-size(gf-cache-info-users(cache-info)) else 0 end if else 0 end if end method; define method cache-header-size (e :: ) => (res :: ) instance-size(e) end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); unless (debug-name(object-class(e)) == #"") dws-size(dws) := dws-size(dws) + cache-header-size(e); end unless; dispatch-walker(dws, cache-header-engine-node-next(e), f, cost, hits) end method; define method dispatch-walker (dws :: , e :: , f :: , cost :: , hits :: ) => (hits :: ) f(e); dws-size(dws) := dws-size(dws) + instance-size(e); // one more for next with-preserved-dispatch-walking-types (dws) for (i from 0 below domain-number-required(e)) dws-arg-types(dws)[i] := domain-type(e, i); dws-partial-types(dws)[i] := domain-type(e, i) end for; let next = cache-header-engine-node-next(e); let my-hits = dispatch-walker(dws, next, f, cost, hits); when (instance?(next, )) // no discrimination, count it as hits incf(dws-cache-attempts(dws), my-hits); incf(dws-cache-hits(dws), my-hits); end when; my-hits end; end method;