Таким образом, хотя эти программы выглядят одинаково, они не одинаковы. Вы используете арифметику fixnum в версии C, в то время как версия Scheme использует стандартную числовую башню. Чтобы сделать версию C более похожей на Scheme, попробуйте использовать библиотеку bignum для своих вычислений.
В качестве теста я заменил арифметику на (rnrs arithmetic flonums)
и (rnrs arithmetic fixnums)
, и это вдвое сократило время выполнения в DrRacket. Я ожидаю, что то же самое произойдет в любой реализации.
Теперь мои первоначальные тесты показали, что код C выполняется примерно в 25 раз быстрее, а не в 50 раз, как ожидалось, и, перейдя на арифметику с плавающей запятой, я стал примерно в 15 раз быстрее.
Я думаю, что могу сделать это еще быстрее, используя небезопасные процедуры, поскольку Scheme проверяет тип каждого аргумента во время выполнения, он выполняет операции перед каждой процедурой, чего не происходит в версии C. В качестве теста я изменил его, чтобы использовать небезопасные процедуры в своей реализации, и теперь он работает только в 10 раз медленнее.
Надеюсь, это поможет и в Chez :)
ИЗМЕНИТЬ
Вот мой модифицированный исходник, который увеличивает скорость в 2 раза:
#!r6rs
(import
(rnrs)
;; import the * and + that only work on floats (which are faster, but they still check their arguments)
(only (rnrs arithmetic flonums) fl+ fl*))
(let* ((n (* 1024 16))
(a (make-vector n))
(acc 0.0)) ; We want float, lets tell Scheme about that!
;; using inexact f instead of integer i
;; makes every result of cos and sin inexact
(do ((i 0 (+ i 1))
(f 0.0 (+ f 1)))
((= i n) #f)
(vector-set! a i (cons (cos f) (sin f))))
(do ((i 0 (+ i 1)))
((= i n) #f)
(do ((j 0 (+ j 1)))
((= j n) #f)
(let ((ai (vector-ref a i))
(aj (vector-ref a j)))
;; use float versions of + and *
;; since this is where most of the time is used
(set! acc (fl+ acc
(fl+ (fl* (car ai) (cdr aj))
(fl* (cdr ai) (car aj))))))))
(write acc)
(newline))
И конкретная реализация (блокировка) просто для того, чтобы сказать, что проверка типов, выполненная во время выполнения, действительно имеет влияние, этот код работает на 30% быстрее, чем предыдущая оптимизация:
#lang racket
;; this imports import the * and + for floats as unsafe-fl* etc.
(require racket/unsafe/ops)
(let* ((n (* 1024 16))
(a (make-vector n))
(acc 0.0)) ; We want float, lets tell Scheme about that!
(do ((i 0 (+ i 1))
(f 0.0 (+ f 1)))
((= i n) #f)
;; using inexact f instead of integer i
;; makes every result of cos and sin inexact
(vector-set! a i (cons (cos f) (sin f))))
(do ((i 0 (+ i 1)))
((= i n) #f)
(do ((j 0 (+ j 1)))
((= j n) #f)
;; We guarantee argument is a vector
;; and nothing wrong will happen using unsafe accessors
(let ((ai (unsafe-vector-ref a i))
(aj (unsafe-vector-ref a j)))
;; use unsafe float versions of + and *
;; since this is where most of the time is used
;; also use unsafe car/cdr as we guarantee the argument is
;; a pair.
(set! acc (unsafe-fl+ acc
(unsafe-fl+ (unsafe-fl* (unsafe-car ai) (unsafe-cdr aj))
(unsafe-fl* (unsafe-cdr ai) (unsafe-car aj))))))))
(write acc)
(newline))
Я постарался сохранить стиль исходного кода. Это не очень идиоматическая схема. Например. Я бы вообще не стал использовать set!
, но на скорость это не влияет.
person
Sylwester
schedule
27.09.2018