Так что, хотя эти программы выглядят одинаково, они не одинаковы.Вы используете арифметику fixnum в версии C, в то время как версия Scheme использует стандартную числовую вышку.Чтобы сделать версию C более похожей на Scheme, попробуйте использовать библиотеку bignum для своих расчетов.
В качестве теста я заменил арифметику на (rnrs arithmetic flonums)
и (rnrs arithmetic fixnums)
, и это вдвое сократило время выполнения в DrRacket.Я ожидаю, что то же самое произойдет в любой реализации.
Теперь мои первоначальные тесты показали, что код C выполняется примерно в 25 раз быстрее, а не в 50 раз, как ожидалось, и, переходя к арифметике с плавающей запятой, я до C, который примерно в 15 раз быстрее.
Я думаю, что могу сделать это еще быстрее, используя небезопасные процедуры, поскольку Scheme проверяет тип каждого аргумента во время выполнения, выполняет операции перед каждой процедурой, чего не происходит в C-версии.В качестве теста я изменил его, чтобы использовать небезопасные процедуры в своей реализации, и теперь он работает только в 10 раз медленнее.
Надеюсь, это поможет и в Chez:)
EDIT
Вот мой модифицированный источник, который повышает скорость в 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!
, но это не влияет на скорость.