Próbuję wymyślić pewne problemy z wydajnością, które miałem z Haskellem. W ramach tego napisałem mały program porównawczy, aby porównać C i Haskell. W szczególności przetłumaczyłem program C do Haskella z jak najmniejszymi zmianami, jakie mogłem. Zmierzona w prędkości część programu Haskell jest napisana w bardzo imperatywnym stylu.Dlaczego Haskell działa tak źle podczas wykonywania kodów typu C? (w tym przypadku co najmniej)
Program tworzy dwie listy liczb losowych w pewnym zakresie, a następnie oblicza całkę wykresu utworzoną przez proste połączenie tych punktów, z jedną listą będącą wartością x i jedną listą będącą wartością y. Zasadniczo jest to trapezoidal rule.
Tutaj oba kody:
main.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 5000000
#define maxY 1e5f/N
#define maxXgap 1
int main(){
int i;
float *y, *x;
float xaccum, area;
clock_t begin, end;
double time_spent;
y = (float*)malloc(sizeof(float)*N);
x = (float*)malloc(sizeof(float)*N);
srand(50546345); // change seed for different numbers
//populate y and x fields with random points
for(i = 0; i < N; i++){
y[i] = ((float)rand())/((float)RAND_MAX)*maxY;
}
xaccum = 0;
for(i = 0; i < N; i++){
x[i] = xaccum;
xaccum += ((float)rand())/((float)RAND_MAX)*maxXgap;
}
begin = clock();
//perform a trapezoidal integration using the x y coordinates
area = 0;
for(i = 0; i < N-1; i++){
area += (y[i+1]+y[i])/2*(x[i+1]-x[i]);
}
end = clock();
time_spent = (double)(end - begin)/CLOCKS_PER_SEC * 1000;
printf("%i points\n%f area\n%f ms\n", N, area, time_spent);
}
Main.hs
{-# LANGUAGE BangPatterns #-}
module Main where
import Data.Array.Unboxed
import Data.Array.IO
import Data.List
import System.Random
import System.CPUTime
import Text.Printf
import Control.Exception
main :: IO()
main = do
(x,y) <- initArrays
area <- time $ integrate x y
print area
n :: Int
n = 5000000
maxY :: Float
maxY = 100000.0/(fromIntegral n)
maxXgap :: Float
maxXgap = 1
--initialize arrays with random floats
--this part is not measured in the running time (very slow)
initArrays :: IO (IOUArray Int Float, IOUArray Int Float)
initArrays = do
y <- newListArray (0,n-1) (randomList maxY n (mkStdGen 23432))
x <- newListArray (0,n-1) (scanl1 (+) $ randomList maxXgap n (mkStdGen 5462))
return (x,y)
randomList :: Float -> Int -> StdGen -> [Float]
randomList max n gen = map (abs . ((*) max)) (take n . unfoldr (Just . random) $ gen)
integrate :: IOUArray Int Float -> IOUArray Int Float -> IO Float
integrate x y = iterative x y 0 0
iterative :: IOUArray Int Float -> IOUArray Int Float -> Int -> Float -> IO Float
iterative x y !i !accum = do if i == n-1
then return accum
else do x1 <- readArray x i
x2 <- readArray x (i+1)
y1 <- readArray y i
y2 <- readArray y (i+1)
iterative x y (i+1) (accum + (y2+y1)/2*(x2-x1))
time :: IO t -> IO t
time a = do
start <- getCPUTime
v <- a
end <- getCPUTime
let diff = (fromIntegral (end-start))/(10^9)
printf "Computation time %0.5f ms\n" (diff :: Double)
return v
Integracja C trwa około 7 ms do integracji Haskell około 60 ms w moim systemie. Oczywiście wersja Haskell będzie wolniejsza, ale zastanawiam się, dlaczego jest o wiele wolniej. Oczywiście w kodzie Haskella jest dużo nieefektywności.
Dlaczego kod Haskella jest znacznie wolniejszy? Jak można to naprawić?
Dzięki za wszelkie odpowiedzi.
Opcja 'pakiet array' ma' 'unsafeWrite' unsafeRead' i też nie ma potrzeby, aby przejść do' vector' za to. –
Oh, okay. Spojrzałem szybko, ale nie mogłem ich zobaczyć. Oczywiście zbyt szybko –
@ Daniel Fischer Nie widzę tych metod w interfejsie MArray. Czy naprawdę mogą być zaimplementowane, gdy tablice mogą być indeksowane przez dowolny element implementujący 'Ix'? –