有关更新,请参见编辑1、2和3。我在这里留下完整的研究过程。
我知道我们可以使用未型球拍(反之亦然)的typed/racket
模块。但是,当这样做时,typed/racket
模块的行为就像是typed/racket/no-check
,它可以禁用优化并将其用作普通的未型模块。
例如,如果您有这样的typed/racket
模块:
#lang typed/racket
(require math)
(provide hello)
(define (hello [str : String])
(define result : (Matrix Flonum) (do-some-crazy-matrix-operations))
(display (format "Hello ~a! Result is ~a" str result)))
,您想在这样的未经类似程序中使用它:
#lang racket/base
(require "hello-matrix.rkt")
(hello "Alan Turing")
您将获得相当糟糕的性能结果(在我的情况下,我正在执行大约600000个矩阵乘法,该程序甚至还没有完成),而使用#lang typed/racket
则可以在3秒内完成我的程序完成。
不利的一面是,我的未构想的代码被类型感染,迫使我在TR中写下所有程序,使我很快发疯。
但是我的救主还不是很远。我偶然发现了一个有趣的四月般的包裹杰伊·麦卡锡(Jay McCarthy)在一个阴暗的夜晚,叫做live-free-or-die
,这几乎可以做到这一点:
http://docs.racket-lang.org/live-free-or-die/index.html
#lang racket/base
(require (for-syntax racket/base
typed-racket/utils/tc-utils))
(define-syntax (live-free-or-die! stx)
(syntax-case stx ()
[(_)
(syntax/loc stx
(begin-for-syntax
(set-box! typed-context? #t)))]))
(provide live-free-or-die!
(rename-out [live-free-or-die!
Doctor-Tobin-Hochstadt:Tear-down-this-wall!]))
通过在我的typed/racket
模块中使用它,就像:
#lang racket
(require live-free-or-die)
(live-free-or-die!)
(require math)
(provide hello)
(define (hello str)
(define result (do-some-crazy-matrix-operations))
(display (format "Hello ~a! Result is ~a" str result)))
现在,我的模块不再是#lang typed/racket
,但是运行它的结果是壮观的!它在3秒内运行,就像是typed/racket
模块一样。
我当然对那个黑客感到厌恶,这就是为什么我想知道是否可以更好地解决这个问题,尤其是用于从 math
使用的矩阵操作。
Google组讨论了关于这个疯狂模块的讨论Jay写的是我唯一能获得的信息。
https://groups.google.com/forum/# !! topic/racket-users/jzohyxwwjqu
这个线程中的人似乎说模块不再有用:
MATTHIAS FELLEISEN
好吧,既然我们的年轻人很容易揭穿包裹,我们就可以让它死亡,因为它不再想要生活。
真的有更好的选择吗?
编辑1-可测试的示例
如果要测试性能差异,请尝试使用do-some-crazy-matrix-operations
的此定义:
#lang typed/racket
(require math)
(provide hello)
(: do-some-crazy-matrix-operations : (-> (Matrix Flonum)))
(define (do-some-crazy-matrix-operations)
(define m1 : (Matrix Flonum) (build-matrix 5 5 (lambda (x y) (add1 (random)))))
(define m2 : (Matrix Flonum) (build-matrix 5 5 (lambda (x y) (add1 (random)))))
(for ([i 60000])
(set! m1 (matrix-map * m1 m2))
(set! m2 (matrix-map * m1 m2)))
(matrix+ m1 m2))
(define (hello [str : String])
(define result : (Matrix Flonum) (do-some-crazy-matrix-operations))
(display (format "Hello ~a! Result is ~a" str result)))
(time (hello "Alan Turing"))
使用#lang typed/racket
它在288ms中运行:
cpu time: 288 real time: 286 gc time: 16
使用#lang typed/racket/no-check
它在52秒内运行:
cpu time: 52496 real time: 52479 gc time: 396
使用#lang racket
和live-free-or-die
它在280ms中运行:
cpu time: 280 real time: 279 gc time: 4
编辑2-这不是问题!
在约翰·克莱门特(John Clement)的回答之后,我发现这些例子还不足以复制真正的问题。使用typed/racket
中的模块实际上可以正常工作。
我真正的问题是边界合同由从未型到打字球拍的类创建的问题。
让我们考虑一下hello-matrix.rkt
的实现:
#lang typed/racket
(require math)
(provide hello crazy% Crazy)
(define-type CrazyClass (Class (field [m1 (Matrix Flonum)])
(field [m2 (Matrix Flonum)])
(do (-> (Matrix Flonum)))))
(define-type Crazy (Instance CrazyClass))
(: crazy% CrazyClass)
(define crazy%
(class object%
(field [m1 (build-matrix 5 5 (lambda (x y) (add1 (random))))]
[m2 (build-matrix 5 5 (lambda (x y) (add1 (random))))])
(super-new)
(define/public (do)
(set! m1 (matrix* (matrix-transpose m1) m2))
(set! m2 (matrix* (matrix-transpose m1) m2))
(matrix+ m1 m2))))
(: do-some-crazy-matrix-operations : Crazy -> (Matrix Flonum))
(define (do-some-crazy-matrix-operations crazy)
(for ([i 60000])
(send crazy do))
(matrix+ (get-field m1 crazy) (get-field m2 crazy)))
(define (hello [str : String] [crazy : Crazy])
(define result : (Matrix Flonum) (do-some-crazy-matrix-operations crazy))
(display (format "Hello ~a! Result is ~an" str result)))
然后这两种用法:
#lang typed/racket
(require "hello-matrix.rkt")
(define crazy : Crazy (new crazy%))
(time (hello "Alan Turing" crazy))
cpu time: 1160 real time: 1178 gc time: 68
#lang racket
(require "hello-matrix.rkt")
(define crazy (new crazy%))
(time (hello "Alan Turing" crazy))
cpu time: 7432 real time: 7433 gc time: 80
使用contract-profile
:
Running time is 83.47% contracts
6320/7572 ms
BY CONTRACT
g66 @ #(struct:srcloc hello-matrix.rkt 3 15 50 6)
3258 ms
(-> String (object/c (do (-> any/c (struct/c Array (vectorof Index) Index (box/c (or/c #f #t)) (-> Void) (-> (vectorof Index) Float)))) (field (m1 (struct/c Array (vectorof Index) Index (box/c (or/c #f #t)) (-> Void) (-> (vectorof Index) Float))) (m2 (struct/c Array (vectorof Index) Index (box/c (or/c #f #t)) (-> Void) (-> (vectorof Index) Float))))) any) @ #(struct:srcloc hello-matrix.rkt 3 9 44 5)
3062 ms
编辑3-将struct
从输入到Untyped的传递比通过class
更具性能使用结构而不是类解决以下内容:
hello-matrix.rkt:
#lang typed/racket
(require math)
(provide hello (struct-out crazy))
(struct crazy ([m1 : (Matrix Flonum)] [m2 : (Matrix Flonum)]) #:mutable)
(define-type Crazy crazy)
(define (crazy-do [my-crazy : Crazy])
(set-crazy-m1! my-crazy (matrix* (matrix-transpose (crazy-m1 my-crazy))
(crazy-m2 my-crazy)))
(set-crazy-m2! my-crazy (matrix* (matrix-transpose (crazy-m1 my-crazy))
(crazy-m2 my-crazy)))
(matrix+ (crazy-m1 my-crazy) (crazy-m2 my-crazy)))
(: do-some-crazy-matrix-operations : Crazy -> (Matrix Flonum))
(define (do-some-crazy-matrix-operations my-crazy)
(for ([i 60000])
(crazy-do my-crazy))
(matrix+ (crazy-m1 my-crazy) (crazy-m2 my-crazy)))
(define (hello [str : String] [my-crazy : Crazy])
(define result : (Matrix Flonum) (do-some-crazy-matrix-operations my-crazy))
(display (format "Hello ~a! Result is ~an" str result)))
用法:
#lang typed/racket
(require "hello-matrix.rkt")
(require math)
(define my-crazy (crazy (build-matrix 5 5 (lambda (x y) (add1 (random))))
(build-matrix 5 5 (lambda (x y) (add1 (random))))))
(time (hello "Alan Turing" my-crazy))
cpu time: 1008 real time: 1008 gc time: 52
#lang racket
cpu time: 996 real time: 995 gc time: 52
我将其写为"答案",以便让我格式化我的代码...我认为我们互相谈论彼此。具体来说,我可以在大约半秒钟内从非类型的模块中运行您的键入代码。如您所建议(需要TR模块的一个),并且花费了相同的时间(大约半秒)。让我小心这样说:
" hello-matrix.rkt"的内容:
#lang typed/racket
(require math)
(provide hello)
(: do-some-crazy-matrix-operations : (-> (Matrix Flonum)))
(define (do-some-crazy-matrix-operations)
(define m1 : (Matrix Flonum) (build-matrix 5 5 (lambda (x y) (add1 (random)))))
(define m2 : (Matrix Flonum) (build-matrix 5 5 (lambda (x y) (add1 (random)))))
(for ([i 60000])
(set! m1 (matrix-map * m1 m2))
(set! m2 (matrix-map * m1 m2)))
(matrix+ m1 m2))
(define (hello [str : String])
(define result : (Matrix Flonum) (do-some-crazy-matrix-operations))
(display (format "Hello ~a! Result is ~a" str result)))
(time (hello "Alan Turing"))
然后,我从 untyped 模块中称其为:
#lang racket/base
(require "hello-matrix.rkt")
(time (hello "Alan Turing"))
这是结果:
Hello Alan Turing! Result is (array #[#[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0]])
cpu time: 719 real time: 710 gc time: 231
Hello Alan Turing! Result is (array #[#[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0] #[+inf.0 +inf.0 +inf.0 +inf.0 +inf.0]])
cpu time: 689 real time: 681 gc time: 184
也就是说,它需要同样的时间从未型球拍中调用它与打字球拍一样。
此结果可能取决于您正在使用的Drracket版本。我正在使用6.11。
所有这些都是为了证明TR代码仍然是TR代码,即使您从未经类似的代码调用它也是如此。我确实相信您遇到了性能问题,而且我确实相信它们与矩阵操作有关,但是这个特殊的例子没有说明它们。