On Dec 27, 2012, at 9:22 PM, Neil Toronto wrote: > Sorry it took so long to reply. > > I applied the patch and verified that my example runs a *lot* faster (with > domain and range contracts identical, of course). Unfortunately, a similar > test using arrays isn't any faster. This is how the identity function for > arrays is defined in the test: > > (: array-id (All (A) ((Array A) -> (Array A)))) > (define (array-id arr) arr)
If we had macros for types, we might be able to write (All (A) (let ((t (Array A))) (t -> t))) > The change doesn't help because the contracts for each instance of (Array A) > in the type aren't identical. Merging such contracts would be a great space > optimization in general, but I don't know whether it would make using > `math/array' from untyped code any faster. > > Actually, right now, I'm sure it wouldn't help much. I can't think of any > array functions that return an array with an identical procedure It is not an identical procedure that makes for contract optimizations here but identical contracts/types. Would that help? > as one of the input arrays', except as a performance optimization. On the > other hand, some future code, possibly from someone else, might operate on > lists of arrays, and possibly return a shared tail. Merging equivalent > contracts (or using `contract-stronger?') would help, then. > > Neil ⊥ > > On 12/18/2012 12:04 PM, Robby Findler wrote: >> I can't help with the others, but for 1.) I've considered using a >> contract-stronger? test or even just an eq? test when applying a >> contract, checking what's already on the value. I haven't added that as >> it hasn't come up "in anger" (and it isn't free, but the check is >> cheap). If I put a broken test (diff below) and adjust your example a >> little bit, it runs quickly for me. Does it also help in your larger >> context? >> >> Adjustment: >> >> (module defs racket >> (define c (any/c . -> . any/c)) >> (provide (contract-out [fun-id (-> c c)])) >> (define (fun-id f) f)) >> >> Diff: >> >> [robby@wireless-165-124-98-86] ~/git/plt/collects/racket$ git diff . | cat >> diff --git a/collects/racket/contract/private/arrow.rkt >> b/collects/racket/contract/private/arrow.rkt >> index 55ed632..63c9a6e 100644 >> --- a/collects/racket/contract/private/arrow.rkt >> +++ b/collects/racket/contract/private/arrow.rkt >> @@ -506,6 +506,10 @@ v4 todo: >> partial-mandatory-kwds >> partial-optional-kwds >> partial-ranges)) >> (λ (val) >> + (cond >> + [(eq? (value-contract val) ctc) >> + val] >> + [else >> (if has-rest? >> (check-procedure/more val mtd? dom-length >> mandatory-keywords optional-keywords orig-blame) >> (check-procedure val mtd? dom-length optionals-length >> mandatory-keywords optional-keywords orig-blame)) >> @@ -521,7 +525,7 @@ v4 todo: >> impersonator-prop:contracted ctc >> impersonator-prop:application-mark (cons contract-key >> ;; is this right? >> - partial-ranges))))))) >> + >> partial-ranges)))]))))) >> (define (->-name ctc) >> (single-arrow-name-maker >> >> >> The diff is broken because it doesn't account for blame. The test should >> be making sure that the two contracts blame the same things >> (alternatively, we could not check that and just drop the inner >> contract. That may be better, but not as easy to try ...) >> >> Robby >> >> >> On Tue, Dec 18, 2012 at 12:06 PM, Neil Toronto <neil.toro...@gmail.com >> <mailto:neil.toro...@gmail.com>> wrote: >> >> So there are potentially huge inefficiencies when mixing typed and >> untyped code, usually when functions are passed across the contract >> barrier. Here are a few things that I think cause them. Can the >> People Who Know This Stuff Really Well please comment on these issues? >> >> ==== >> 1. Functions that cross the contract barrier can have exactly the >> same contract applied multiple times. Here's some benchmark code >> that demonstrates the issue: >> >> #lang racket >> >> (module defs racket >> (provide (contract-out [fun-id (-> (any/c . -> . any/c) >> (any/c . -> . any/c))])) >> (define (fun-id f) f)) >> >> (require 'defs) >> >> (define (f x) x) >> >> (define h >> (for/fold ([h f]) ([_ (in-range 1000)]) >> (fun-id h))) >> >> (for ([_ (in-range 5)]) >> (time (for ([_ (in-range 1000)]) >> (h 5)))) >> >> >> I get over 800ms for each 1000 applications of `h', because it's >> basically a 1000-deep wrapper. (Or is it 2000?) >> >> (The contract system is smart enough to check the contract quickly >> when the types are (any/c . -> . any), but I don't think TR ever >> generates contracts using `any'.) >> >> This is a problem for `math/array' because array procedures are >> wrapped going in and going out with exactly the same contract: >> ((vectorof index?) . -> . any/c). >> >> ==== >> 2. It seems TR checks more things than it really has to. In this >> example, the predicate `foo?' prints something so we can observe >> when it's applied, and is used as a predicate for an opaque type >> whose values cross the contract barrier: >> >> #lang racket >> >> ;; Provides a predicate and constructor for the opaque type `Foo' >> (module foo-defs racket >> (provide foo? make-foo) >> >> (define (make-foo x) x) >> >> (define (foo? x) >> (printf "foo?~n") >> (exact-integer? x)) >> ) >> >> (module typed-defs typed/racket >> (provide get-foo foo-ap) >> >> (require/typed >> (submod ".." foo-defs) >> [#:opaque Foo foo?] >> [make-foo (Integer -> Foo)]) >> >> ;; prints `foo?' 10 times; definitely necessary >> (define foos (build-vector 10 make-foo)) >> >> (: get-foo (Integer -> Foo)) >> (define (get-foo i) >> (vector-ref foos i)) >> >> (: foo-ap (All (A) ((Foo -> A) Foo -> A))) >> (define (foo-ap f foo) >> (f foo)) >> ) >> >> (require 'typed-defs) >> >> >> I don't understand why the contract for `get-foo' has to check the >> return value, because TR already ensures that `get-foo' always >> returns a `Foo': >> >> (printf "going to get a foo~n") >> (get-foo 5) ; prints `foo?' once; why? >> >> Could TR generate (exact-integer? . -> . any) for `get-foo'? >> >> Relatedly, TR already ensures that the function passed to `foo-ap' >> is only applied to `Foo' values, but this is also checked by a contract: >> >> (printf "going to apply a function to a foo~n") >> (foo-ap identity 1) ; prints `foo?' twice; why not once, just for 1? >> >> ==== >> 3. It's a shame we can't know statically whether an untyped function >> will return more than one value. >> >> Suppose we could, and that TR generated contracts with `any/c' >> argument types for higher-order functions (i.e. fix issue #2). Then >> array procedures passed from untyped to typed code could have the >> contract (any/c . -> . any), which is checked immediately. >> >> There's probably more, but this will do for now. >> >> Neil ⊥ >> _________________________ >> Racket Developers list: >> http://lists.racket-lang.org/__dev <http://lists.racket-lang.org/dev> >> >> > > _________________________ > Racket Developers list: > http://lists.racket-lang.org/dev _________________________ Racket Developers list: http://lists.racket-lang.org/dev