मैं हास्केल में एक गेम लिख रहा हूं, और यूआई में मेरे वर्तमान पास में ज्यामिति की बहुत सारी प्रक्रियात्मक पीढ़ी शामिल है। मैं वर्तमान में एक विशेष आपरेशन (सी-ish स्यूडोकोड) के प्रदर्शन की पहचान करने पर ध्यान केंद्रित कर रहा हूँ:मल्टीप्ली-एडे ऑपरेशन पर हास्केल गणित प्रदर्शन
Vec4f multiplier, addend;
Vec4f vecList[];
for (int i = 0; i < count; i++)
vecList[i] = vecList[i] * multiplier + addend;
है यही कारण है, एक दलदल की मानक चार तैरता के गुणा-ऐड, बात SIMD अनुकूलन के लिए परिपक्व की तरह।
परिणाम ओपनजीएल वर्टेक्स बफर पर जा रहा है, इसलिए इसे अंततः एक फ्लैट सी सरणी में डालना होगा। इसी कारण से, गणना सी 'फ्लोट' प्रकारों पर संभवतः की जानी चाहिए।
मैंने हास्केल में इस तरह की चीज को जल्दी से करने के लिए या तो लाइब्रेरी या देशी बेवकूफ समाधान की तलाश की है, लेकिन मेरे द्वारा आने वाले हर समाधान के प्रदर्शन के लगभग 2% (यानी, 50x धीमी गति से) जीसीसी से सी की तुलना में धीमी झंडे के साथ। अनुमोदित, मैंने कुछ हफ्ते पहले हास्केल के साथ शुरुआत की, इसलिए मेरा अनुभव सीमित है-यही कारण है कि मैं आप लोगों के पास आ रहा हूं। क्या आप में से कोई भी तेज हास्केल कार्यान्वयन के लिए सुझाव प्रदान कर सकता है, या उच्च प्रदर्शन वाले हास्केल कोड को लिखने के तरीके पर दस्तावेज़ों के लिए पॉइंटर्स?
पहला, सबसे हालिया हास्केल समाधान (लगभग 12 सेकंड घड़ियों)। मैंने this SO post से बैंग-पैटर्न सामग्री की कोशिश की, लेकिन इससे AFAICT में कोई फर्क नहीं पड़ता। '(\ I v -> v * 4)' के साथ 'multAdd' को प्रतिस्थापित करने से निष्पादन समय 1.9 सेकेंड तक चला गया, इसलिए बिटवाई सामान (और स्वचालित अनुकूलन के लिए परिणामी चुनौतियां) गलती पर बहुत अधिक प्रतीत नहीं होती हैं।
{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -O2 -fvia-C -optc-O3 -fexcess-precision -optc-march=native #-}
import Data.Vector.Storable
import qualified Data.Vector.Storable as V
import Foreign.C.Types
import Data.Bits
repCount = 10000
arraySize = 20000
a = fromList $ [0.2::CFloat, 0.1, 0.6, 1.0]
m = fromList $ [0.99::CFloat, 0.7, 0.8, 0.6]
multAdd :: Int -> CFloat -> CFloat
multAdd !i !v = v * (m ! (i .&. 3)) + (a ! (i .&. 3))
multList :: Int -> Vector CFloat -> Vector CFloat
multList !count !src
| count <= 0 = src
| otherwise = multList (count-1) $ V.imap multAdd src
main = do
print $ Data.Vector.Storable.sum $ multList repCount $
Data.Vector.Storable.replicate (arraySize*4) (0::CFloat)
यहां मेरे पास सी है। इस कोड में कुछ #ifdefs हैं जो इसे सीधे संकलित करने से रोकते हैं; परीक्षण चालक के लिए नीचे स्क्रॉल करें।
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef float v4fs __attribute__ ((vector_size (16)));
typedef struct { float x, y, z, w; } Vector4;
void setv4(v4fs *v, float x, float y, float z, float w) {
float *a = (float*) v;
a[0] = x;
a[1] = y;
a[2] = z;
a[3] = w;
}
float sumv4(v4fs *v) {
float *a = (float*) v;
return a[0] + a[1] + a[2] + a[3];
}
void vecmult(v4fs *MAYBE_RESTRICT s, v4fs *MAYBE_RESTRICT d, v4fs a, v4fs m) {
for (int j = 0; j < N; j++) {
d[j] = s[j] * m + a;
}
}
void scamult(float *MAYBE_RESTRICT s, float *MAYBE_RESTRICT d,
Vector4 a, Vector4 m) {
for (int j = 0; j < (N*4); j+=4) {
d[j+0] = s[j+0] * m.x + a.x;
d[j+1] = s[j+1] * m.y + a.y;
d[j+2] = s[j+2] * m.z + a.z;
d[j+3] = s[j+3] * m.w + a.w;
}
}
int main() {
v4fs a, m;
v4fs *s, *d;
setv4(&a, 0.2, 0.1, 0.6, 1.0);
setv4(&m, 0.99, 0.7, 0.8, 0.6);
s = calloc(N, sizeof(v4fs));
d = s;
double start = clock();
for (int i = 0; i < M; i++) {
#ifdef COPY
d = malloc(N * sizeof(v4fs));
#endif
#ifdef VECTOR
vecmult(s, d, a, m);
#else
Vector4 aa = *(Vector4*)(&a);
Vector4 mm = *(Vector4*)(&m);
scamult((float*)s, (float*)d, aa, mm);
#endif
#ifdef COPY
free(s);
s = d;
#endif
}
double end = clock();
float sum = 0;
for (int j = 0; j < N; j++) {
sum += sumv4(s+j);
}
printf("%-50s %2.5f %f\n\n", NAME,
(end - start)/(double) CLOCKS_PER_SEC, sum);
}
यह स्क्रिप्ट संकलन और कई जीसीसी ध्वज संयोजनों के साथ परीक्षण चलाएगी। सबसे अच्छा प्रदर्शन मेरे सिस्टम पर cmath-64-native-O3-limit-vector-nocopy द्वारा 0.22 सेकंड ले रहा था।
import System.Process
import GHC.IOBase
cBase = ("cmath", "gcc mult.c -ggdb --std=c99 -DM=10000 -DN=20000")
cOptions = [
[("32", "-m32"), ("64", "-m64")],
[("generic", ""), ("native", "-march=native -msse4")],
[("O1", "-O1"), ("O2", "-O2"), ("O3", "-O3")],
[("restrict", "-DMAYBE_RESTRICT=__restrict__"),
("norestrict", "-DMAYBE_RESTRICT=")],
[("vector", "-DVECTOR"), ("scalar", "")],
[("copy", "-DCOPY"), ("nocopy", "")]
]
-- Fold over the Cartesian product of the double list. Probably a Prelude function
-- or two that does this, but hey. The 'perm' referred to permutations until I realized
-- that this wasn't actually doing permutations. '
permfold :: (a -> a -> a) -> a -> [[a]] -> [a]
permfold f z [] = [z]
permfold f z (x:xs) = concat $ map (\a -> (permfold f (f z a) xs)) x
prepCmd :: (String, String) -> (String, String) -> (String, String)
prepCmd (name, cmd) (namea, cmda) =
(name ++ "-" ++ namea, cmd ++ " " ++ cmda)
runCCmd name compileCmd = do
res <- system (compileCmd ++ " -DNAME=\\\"" ++ name ++ "\\\" -o " ++ name)
if res == ExitSuccess
then do system ("./" ++ name)
return()
else putStrLn $ name ++ " did not compile"
main = do
mapM_ (uncurry runCCmd) $ permfold prepCmd cBase cOptions
अधिक बेवकूफ प्रकारों का उपयोग करने के लिए रिवाइटिंग लगभग मोटे तौर पर रनटाइम, http://hpaste.org/fastcgi/hpaste.fcgi/view?id=26551#a26551 लेकिन मैं हूं रोमन को विचार करने के लिए इसे अग्रेषित करना। –