gpt4 book ai didi

matrix - 为什么在非类型化模块中使用类型化/ Racket 模块中的类会产生糟糕的性能?

转载 作者:行者123 更新时间:2023-12-04 21:05:18 24 4
gpt4 key购买 nike

See EDIT 1, 2, and 3 for updates. I leave here the complete research process.



我知道我们可以使用 typed/racket来自无类型 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")

在使用 #lang typed/racket 时,你会得到非常糟糕的性能结果(在我的例子中,我正在做大约 600000 次矩阵乘法,程序甚至没有完成)让我的程序在 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 Groups 关于 Jay 写的那个疯狂模块的讨论是我能得到的唯一信息。

https://groups.google.com/forum/#!topic/racket-users/JZoHYxwwJqU

该线程中的人似乎说该模块不再有用:

Matthias Felleisen
Well, now that our youngsters have easily debunked the package, we can let it die because it no longer wants to live.



真的有更好的选择吗?

编辑 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它在 288 毫秒内运行:
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 racketlive-free-or-die它在 280 毫秒内运行:
cpu time: 280 real time: 279 gc time: 4

编辑 2 - 这不是问题!

按照约翰克莱门特的回答,我发现这些例子不足以重现真正的问题。 使用 typed/racket无类型模块中的模块实际上工作正常。

我真正的问题是 的问题。边界契约(Contract) 由从无类型传递到有类型 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 ~a\n" 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从 typed 到 untyped 比通过 class 更高效

使用结构而不是类可以解决此问题:

你好矩阵.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 ~a\n" 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

最佳答案

我将其写为“答案”,以允许我格式化我的代码......我认为我们正在谈论彼此。具体来说,我可以在大约半秒内从一个非类型化模块运行你的类型化代码。我按照您的建议将您的键入代码文件命名为“hello-matrix.rkt”,然后运行您提供的无类型模块
(需要 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"))

然后,我从一个无类型的模块中调用它,就像你说的那样:
#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

也就是说,从无类型的 Racket 中调用它所花费的时间与从有类型的 Racket 中调用它所花费的时间相同。

此结果可能取决于您使用的 DrRacket 版本;我正在使用 6.11。

所有这些都是为了证明 TR 代码仍然是 TR 代码,即使您从无类型代码中调用它也是如此。我确实相信您遇到了性能问题,并且我确实相信它们与矩阵运算有关,但是这个特定示例并未说明它们。

关于matrix - 为什么在非类型化模块中使用类型化/ Racket 模块中的类会产生糟糕的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47507264/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com