
Hello, I've been playing around with Dan Piponi's work on automatic differentiation (from http://sigfpe.blogspot.com/2005/07/automatic-differentiation.html and http://sigfpe.blogspot.com/2006/09/practical-synthetic-differential.html) and I'm getting some odd behavior with the inferred types of functions in GHCi 6.4.2. My test case is as follows:
data Dual a = D a a deriving (Show,Eq)
instance Num a => Num (Dual a) where fromInteger i = D (fromInteger i) 0 (D a a')+(D b b') = D (a+b) (a'+b') (D a a')-(D b b') = D (a-b) (a'-b') (D a a')*(D b b') = D (a*b) (a*b'+a'*b)
evalDeriv f x = (v, v') where D v v' = f (D x 1)
f x = x^3
f' = snd . (evalDeriv f)
When I load this in GHCi, I get: *Main> :t f f :: (Num a) => a -> a *Main> :t snd . (evalDeriv f) snd . (evalDeriv f) :: (Num a, Num (Dual a)) => a -> a *Main> :t f' f' :: Integer -> Integer Why is the type of f' Integer -> Integer, especially when the type of the expression it's defined as is more general? Is this something I'm not understanding about Haskell, or is it more to do with GHC 6.4.2 specifically? Any help appreciated, --Grady Lemoine

Grady Lemoine wrote:
Hello,
I've been playing around with Dan Piponi's work on automatic differentiation (from http://sigfpe.blogspot.com/2005/07/automatic-differentiation.html and http://sigfpe.blogspot.com/2006/09/practical-synthetic-differential.html) and I'm getting some odd behavior with the inferred types of functions in GHCi 6.4.2. My test case is as follows:
data Dual a = D a a deriving (Show,Eq)
instance Num a => Num (Dual a) where fromInteger i = D (fromInteger i) 0 (D a a')+(D b b') = D (a+b) (a'+b') (D a a')-(D b b') = D (a-b) (a'-b') (D a a')*(D b b') = D (a*b) (a*b'+a'*b)
evalDeriv f x = (v, v') where D v v' = f (D x 1)
f x = x^3
f' = snd . (evalDeriv f)
When I load this in GHCi, I get:
*Main> :t f f :: (Num a) => a -> a *Main> :t snd . (evalDeriv f) snd . (evalDeriv f) :: (Num a, Num (Dual a)) => a -> a *Main> :t f' f' :: Integer -> Integer
Why is the type of f' Integer -> Integer, especially when the type of the expression it's defined as is more general? Is this something I'm not understanding about Haskell, or is it more to do with GHC 6.4.2 specifically?
Any help appreciated,
--Grady Lemoine
You have two different things making this error possible. First there is the default(Integer,Int) that Haskell implicitly provides. Second, there is the monomorphism restriction. Try this:
default ()
data Dual a = D a a deriving (Show,Eq)
instance Num a => Num (Dual a) where fromInteger i = D (fromInteger i) 0 (D a a')+(D b b') = D (a+b) (a'+b') (D a a')-(D b b') = D (a-b) (a'-b') (D a a')*(D b b') = D (a*b) (a*b'+a'*b) abs _ = error "no abs" signum _ = error "no signum"
evalDeriv f x = (v, v') where D v v' = f (D x 1)
f x = x^(3::Int)
f' :: (Num a) => a -> a f' = snd . (evalDeriv f)
I played with such things, as seen on the old wiki: http://haskell.org/hawiki/ShortExamples_2fSymbolDifferentiation -- Chris

On 12/2/06, Chris Kuklewicz
Grady Lemoine wrote:
I've been playing around with Dan Piponi's work on automatic differentiation... I played with such things, as seen on the old wiki: http://haskell.org/hawiki/ShortExamples_2fSymbolDifferentiation
I missed this while I was away on vacation so apologies for responding to something two weeks old. I just want to point out that the method I've been promoting is very distinct from symbolic differentiation. I wouldn't make a big deal about this except that besides playing well with Haskell, AD can help with many real world numerical problems, but people often dismiss it because it they confuse it with symbolic differentiation which they have already determined doesn't solve their problems. -- Dan

I understand the distinction, and I find both techniques interesting,
but your AD code is a lot more convenient. All I have to do is make
sure that my functions are sufficiently polymorphic, and I can get
derivatives *automatically* with very little programmer effort. It
seems like a good way to inject worry-free calculus wherever it's
needed -- for instance, if I want gradient information about a
function, I can use AD and avoid having to choose a length scale,
consider accuracy issues, etc. like I would if I used a finite
difference approximation.
(Of course, this assumes that the function I'm differentiating
consists only of building blocks that I have AD definitions of. In
particular, I'm not really sure how I want to define the Ord
operations for Dual, so that a function with an if statement based on
a comparison (e.g. Gaussian elimination with pivoting) can be
differentiated. On the one hand, I'm not sure I want to include the
infinitesimal part in the definition, because it is in some sense how
fast the number is changing, not how big it is; on the other hand, if
I don't include the infinitesimal part, my definition of Ord won't be
consistent with equality...)
One question I have, though, for anyone on the list who knows the
answer -- if I give a function a polymorphic type signature, can it
affect performance? That is, if I write two functions with the same
definitions but different user-specified type signatures,
foo1 :: (Floating a) => a -> a
foo1 = ...
and
foo2 :: Double -> Double
foo2 = ...
when I apply foo1 to a Double, will the compiler (GHC, specifically)
generate code that is just as efficient as if I used foo2?
Regards,
--Grady Lemoine
On 12/19/06, Dan Piponi
On 12/2/06, Chris Kuklewicz
wrote: Grady Lemoine wrote:
I've been playing around with Dan Piponi's work on automatic differentiation... I played with such things, as seen on the old wiki: http://haskell.org/hawiki/ShortExamples_2fSymbolDifferentiation
I missed this while I was away on vacation so apologies for responding to something two weeks old. I just want to point out that the method I've been promoting is very distinct from symbolic differentiation. I wouldn't make a big deal about this except that besides playing well with Haskell, AD can help with many real world numerical problems, but people often dismiss it because it they confuse it with symbolic differentiation which they have already determined doesn't solve their problems. -- Dan _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 12/28/06, Grady Lemoine
One question I have, though, for anyone on the list who knows the answer -- if I give a function a polymorphic type signature, can it affect performance? That is, if I write two functions with the same definitions but different user-specified type signatures,
foo1 :: (Floating a) => a -> a foo1 = ...
and
foo2 :: Double -> Double foo2 = ...
when I apply foo1 to a Double, will the compiler (GHC, specifically) generate code that is just as efficient as if I used foo2?
As with so many things, the answer is "it depends". The simple answer to "if I give a function a polymorphic type signature, can it affect performance?" is "no, because type information is erased before code generation". However, if by "polymorphic type signatures" you mean ones involving class-based overloading, like the ones you wrote, then the answer is "maybe" -- functions that have classes involved in their types are desugared by the compiler into functions that take extra "dictionary" arguments, which can make performance worse. GHC does "specialization" to try to negate some of the performance impact of this, but it doesn't always do what you want it to. The best thing to do is to make sure you compile your code with -O2 and if profiling seems to imply that overloaded functions are causing you bottlenecks, seek help on this list or on Haskell IRC (and there should be a pretty good body of previous discussion on the subject in the list archives). Cheers, Kirsten -- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "I eat too much / I laugh too long / I like too much of you when I'm gone." -- Ani DiFranco

That's interesting. I suppose it makes sense -- for polymorphism that
doesn't involve typeclasses, nothing is assumed about what kind of
data you have, so there's nothing you can do with it that isn't
generic to all data types. I was wondering if the compiler would
automatically generate specialized code for certain concrete instances
of a typeclass (Double) in addition to generic code for an arbitrary
member of the typeclass (Floating a), and it sounds like I may or may
not get that in the way I want. I suppose there may have to be some
slowdown -- if the compiler specializes every piece of code for every
instance of a typeclass it might encounter, it could bloat the
executable beyond all reason. I'll have to do some tests to see if I
notice an effect in practice.
Thank you,
--Grady Lemoine
On 12/28/06, Kirsten Chevalier
On 12/28/06, Grady Lemoine
wrote: One question I have, though, for anyone on the list who knows the answer -- if I give a function a polymorphic type signature, can it affect performance? That is, if I write two functions with the same definitions but different user-specified type signatures,
foo1 :: (Floating a) => a -> a foo1 = ...
and
foo2 :: Double -> Double foo2 = ...
when I apply foo1 to a Double, will the compiler (GHC, specifically) generate code that is just as efficient as if I used foo2?
As with so many things, the answer is "it depends". The simple answer to "if I give a function a polymorphic type signature, can it affect performance?" is "no, because type information is erased before code generation". However, if by "polymorphic type signatures" you mean ones involving class-based overloading, like the ones you wrote, then the answer is "maybe" -- functions that have classes involved in their types are desugared by the compiler into functions that take extra "dictionary" arguments, which can make performance worse. GHC does "specialization" to try to negate some of the performance impact of this, but it doesn't always do what you want it to. The best thing to do is to make sure you compile your code with -O2 and if profiling seems to imply that overloaded functions are causing you bottlenecks, seek help on this list or on Haskell IRC (and there should be a pretty good body of previous discussion on the subject in the list archives).
Cheers, Kirsten
-- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "I eat too much / I laugh too long / I like too much of you when I'm gone." -- Ani DiFranco

Hello Grady, Friday, December 29, 2006, 10:12:12 AM, you wrote:
not get that in the way I want. I suppose there may have to be some slowdown -- if the compiler specializes every piece of code for every instance of a typeclass it might encounter, it could bloat the executable beyond all reason. I'll have to do some tests to see if I notice an effect in practice.
yes. ghc makes specialization only in cases when you used SPECIALIZE/INLINE pragma and when function is small enough to be inlined. otherwise, each (+) operation will make a function call which is very slow thing in Haskell i propose you to use INLINE pragma: {-# INLINE foo #-} unless your function is recursive. in this case, you should use SPECIALIZE pragma: {-# SPECIALIZE foo :: Double -> Double -> Double #-} -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 12/29/06, Bulat Ziganshin
i propose you to use INLINE pragma:
{-# INLINE foo #-}
unless your function is recursive. in this case, you should use SPECIALIZE pragma:
{-# SPECIALIZE foo :: Double -> Double -> Double #-}
I suggest *not* using these pragmas unless a combination of profiling and reading intermediate code dumps suggests that foo -- and its un-specialized nature -- is truly a bottleneck. Excessive amounts of SPECIALIZE pragmas can make your code ugly without actually improving performance if you optimize prematurely (and I speak from experience). Think *first*, add pragmas later; again, people on the mailing lists and IRC channel are usually happy to provide guidance with this. Cheers, Kirsten -- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "To be free is not to have the power to do anything you like; it is to be able to surpass the given towards an open future..."--Simone de Beauvoir

I've performed some experiments in GHCi, and it looks like even for a simple function (+) (which should be the worst case, since if the computation is simple, any extra time required to dispatch the call will show up more strongly in comparison) it doesn't really matter. I get essentially the same execution times no matter which of the definitions below I use, although sometimes one time (apparently at random) is 2-3 times as large as the others; I presume this is the garbage collector at work, or something. Given these results, I'm inclined to make my function types as general as possible, with typeclasses galore, and only use pragmas if profiling reveals a good reason to. I'm attaching my test code for reference. Clumsy noob Haskell code below (I'm still pretty new to Haskell, and this is the first time I've programmed in a monad): ************************************************************************ TypeClassTest.lhs Test of what effect (if any) using a typeclass in GHC has on performance ************************************************************************
module TypeClassTest where import System.CPUTime
l :: [Double] l = [0.0,1.0..1e5]
Fully specialized:
addDouble :: Double -> Double -> Double addDouble = (+)
Generic, but with inlining:
{-# INLINE addInline #-} addInline :: Num a => a -> a -> a addInline = (+)
Generic, but with specialization:
{-# SPECIALIZE addSpecialize :: Double -> Double -> Double #-} addSpecialize :: Num a => a -> a -> a addSpecialize = (+)
Generic, with no compiler pragmas:
addGeneric :: Num a => a -> a -> a addGeneric = (+)
main :: IO () main = do putStrLn $ "Summing " ++ length l ++ " floating-point values in various ways..." foldTime "Double list with addDouble" addDouble l foldTime "Double list with addInline" addInline l foldTime "Double list with addSpecialized" addSpecialize l foldTime "Double list with addGeneric" addGeneric l return ()
foldTime :: String -> (a -> a -> a) -> [a] -> IO a foldTime desc f l = do start <- getCPUTime result <- (return $! foldr1 f l) end <- getCPUTime putStrLn $ "Time for " ++ desc ++ " per list element: " ++ show ((end-start) `div` (fromIntegral $ length l)) return result
--Grady Lemoine
On 12/29/06, Kirsten Chevalier
On 12/29/06, Bulat Ziganshin
wrote: i propose you to use INLINE pragma:
{-# INLINE foo #-}
unless your function is recursive. in this case, you should use SPECIALIZE pragma:
{-# SPECIALIZE foo :: Double -> Double -> Double #-}
I suggest *not* using these pragmas unless a combination of profiling and reading intermediate code dumps suggests that foo -- and its un-specialized nature -- is truly a bottleneck. Excessive amounts of SPECIALIZE pragmas can make your code ugly without actually improving performance if you optimize prematurely (and I speak from experience). Think *first*, add pragmas later; again, people on the mailing lists and IRC channel are usually happy to provide guidance with this.
Cheers, Kirsten
-- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "To be free is not to have the power to do anything you like; it is to be able to surpass the given towards an open future..."--Simone de Beauvoir

Before you start adding pragmas, try compiling with -O, it does a lot of the specialization automatically. -- Lennart On Dec 29, 2006, at 15:00 , Grady Lemoine wrote:
I've performed some experiments in GHCi, and it looks like even for a simple function (+) (which should be the worst case, since if the computation is simple, any extra time required to dispatch the call will show up more strongly in comparison) it doesn't really matter. I get essentially the same execution times no matter which of the definitions below I use, although sometimes one time (apparently at random) is 2-3 times as large as the others; I presume this is the garbage collector at work, or something. Given these results, I'm inclined to make my function types as general as possible, with typeclasses galore, and only use pragmas if profiling reveals a good reason to.
I'm attaching my test code for reference. Clumsy noob Haskell code below (I'm still pretty new to Haskell, and this is the first time I've programmed in a monad):
********************************************************************** ** TypeClassTest.lhs Test of what effect (if any) using a typeclass in GHC has on performance ********************************************************************** **
module TypeClassTest where import System.CPUTime
l :: [Double] l = [0.0,1.0..1e5]
Fully specialized:
addDouble :: Double -> Double -> Double addDouble = (+)
Generic, but with inlining:
{-# INLINE addInline #-} addInline :: Num a => a -> a -> a addInline = (+)
Generic, but with specialization:
{-# SPECIALIZE addSpecialize :: Double -> Double -> Double #-} addSpecialize :: Num a => a -> a -> a addSpecialize = (+)
Generic, with no compiler pragmas:
addGeneric :: Num a => a -> a -> a addGeneric = (+)
main :: IO () main = do putStrLn $ "Summing " ++ length l ++ " floating-point values in various ways..." foldTime "Double list with addDouble" addDouble l foldTime "Double list with addInline" addInline l foldTime "Double list with addSpecialized" addSpecialize l foldTime "Double list with addGeneric" addGeneric l return ()
foldTime :: String -> (a -> a -> a) -> [a] -> IO a foldTime desc f l = do start <- getCPUTime result <- (return $! foldr1 f l) end <- getCPUTime putStrLn $ "Time for " ++ desc ++ " per list element: " ++ show ((end-start) `div` (fromIntegral $ length l)) return result
--Grady Lemoine
On 12/29/06, Kirsten Chevalier
wrote: On 12/29/06, Bulat Ziganshin
wrote: i propose you to use INLINE pragma:
{-# INLINE foo #-}
unless your function is recursive. in this case, you should use SPECIALIZE pragma:
{-# SPECIALIZE foo :: Double -> Double -> Double #-}
I suggest *not* using these pragmas unless a combination of profiling and reading intermediate code dumps suggests that foo -- and its un-specialized nature -- is truly a bottleneck. Excessive amounts of SPECIALIZE pragmas can make your code ugly without actually improving performance if you optimize prematurely (and I speak from experience). Think *first*, add pragmas later; again, people on the mailing lists and IRC channel are usually happy to provide guidance with this.
Cheers, Kirsten
-- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "To be free is not to have the power to do anything you like; it is to be able to surpass the given towards an open future..."--Simone de Beauvoir
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Hello Grady, Friday, December 29, 2006, 11:00:42 PM, you wrote:
I've performed some experiments in GHCi, and it looks like even for a
get essentially the same execution times no matter which of the definitions below I use
you should compare ghc -O2 times, ghci is very different beast. and even such test don't show actual results - as i already said, ghc automatically inlines only small functions -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

I tried compiling, but I got a linker error:
/usr/lib/ghc-6.4.2/libHSrts.a(Main.o): In function `main':
(.text+0x2): undefined reference to `__stginit_ZCMain'
/usr/lib/ghc-6.4.2/libHSrts.a(Main.o): In function `main':
(.text+0x16): undefined reference to `ZCMain_main_closure'
collect2: ld returned 1 exit status
--Grady
On 12/30/06, Bulat Ziganshin
Hello Grady,
Friday, December 29, 2006, 11:00:42 PM, you wrote:
I've performed some experiments in GHCi, and it looks like even for a
get essentially the same execution times no matter which of the definitions below I use
you should compare ghc -O2 times, ghci is very different beast. and even such test don't show actual results - as i already said, ghc automatically inlines only small functions
-- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 12/30/06, Grady Lemoine
I tried compiling, but I got a linker error:
/usr/lib/ghc-6.4.2/libHSrts.a(Main.o): In function `main': (.text+0x2): undefined reference to `__stginit_ZCMain' /usr/lib/ghc-6.4.2/libHSrts.a(Main.o): In function `main': (.text+0x16): undefined reference to `ZCMain_main_closure' collect2: ld returned 1 exit status
You need to either define a "main" function in your module (e.g., "main = putStrLn "Hello world!") or add -c to your compile flags. Cheers, Kirsten -- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "I wanna live for life and no other / cause I don't ever wanna be like my mother I wanna learn to walk on the water / cause I don't wanna be like my father" -- Noe Venable

Hello Kirsten, Friday, December 29, 2006, 6:30:22 PM, you wrote:
I suggest *not* using these pragmas unless a combination of profiling and reading intermediate code dumps suggests that foo -- and its un-specialized nature -- is truly a bottleneck.
it's a matter of taste - and experience. may be it's simpler to add a huge number of INLINE pragmas than to profile and especially read code dumps? -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Maybe it's simpler to add a lot of INLINE, but that can make a program slower as well as faster. It's much better to profile and add them where they are needed. -- Lennart On Dec 30, 2006, at 08:42 , Bulat Ziganshin wrote:
Hello Kirsten,
Friday, December 29, 2006, 6:30:22 PM, you wrote:
I suggest *not* using these pragmas unless a combination of profiling and reading intermediate code dumps suggests that foo -- and its un-specialized nature -- is truly a bottleneck.
it's a matter of taste - and experience. may be it's simpler to add a huge number of INLINE pragmas than to profile and especially read code dumps?
-- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Hello Lennart, Saturday, December 30, 2006, 5:27:01 PM, you wrote:
Maybe it's simpler to add a lot of INLINE, but that can make a program slower as well as faster.
i think that probability of this is much lower :) if you don't like pragmas you may try to find other arguments ;) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Oh, I have other arguments against pragmas. :) But I think the best one is that optimization applied in the wrong place is just poor software engineering. As Michael A. Jackson said: The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet. -- Lennart On Dec 31, 2006, at 04:12 , Bulat Ziganshin wrote:
Hello Lennart,
Saturday, December 30, 2006, 5:27:01 PM, you wrote:
Maybe it's simpler to add a lot of INLINE, but that can make a program slower as well as faster.
i think that probability of this is much lower :) if you don't like pragmas you may try to find other arguments ;)
-- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Dec 31, 2006, at 6:48 , Lennart Augustsson wrote:
Oh, I have other arguments against pragmas. :) But I think the best one is that optimization applied in the wrong place is just poor software engineering.
As Michael A. Jackson said: The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.
/me is moderately mystified that an experienced programmer hasn't discovered that one the hard way yet. -- brandon s. allbery [linux,solaris,freebsd,perl] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Hello Lennart, Sunday, December 31, 2006, 2:48:01 PM, you wrote:
Oh, I have other arguments against pragmas. :) But I think the best one is that optimization applied in the wrong place is just poor software engineering.
As Michael A. Jackson said: The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.
this don't say anything place. and these rules have their own source: it's hard to optimize using your path. but when program optimization is just adding a few options/pragmas to the program, it' becomes cheap enough to change these rules. didn't you thought about it? -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 12/31/06, Bulat Ziganshin
this don't say anything place. and these rules have their own source: it's hard to optimize using your path. but when program optimization is just adding a few options/pragmas to the program, it' becomes cheap enough to change these rules. didn't you thought about it?
In my experience, adding pragmas and toying with options without insight into what they do is not "cheap", because it takes up the programmer's time, and time is more important than anything else. Every minute spent typing in pragmas is a minute lost that could have been spent thinking about how to write your code more elegantly, and in my experience -- and again, maybe it's just that I'm slow -- adding pragmas doesn't help. When it comes to inlining and specializing, GHC tends to be smarter than I am. (Once more, maybe it's just that I'm slow.) I'd rather focus my energies on doing the things GHC can't (usually) do, like replacing an O(n^2) algorithm with an O(log n) algorithm. Cheers, Kirsten -- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "Happy is all in your head / When you wake up and you're not dead / It's a sign of maturation / That you've lowered your expectations..."--Barbara Kessler

What Kirsten said. I think you can be much more productive in optimizing your code if you actually understand what's going on. I usually don't go as far as looking at compiler intermediate code; I usually stick with profiling (or look at assembly code if it's a really performance critical inner loop). Then you can start optimizing. That can be by changing algorithm, changing data representation, strictness annotations, etc. It can also be by inserting some INLINE or SPECIALIZE pragmas, but that's more rare (don't get me wrong about those pragmas, I introduced them in Haskell with hbc). But I think just adding pragmas willy-nilly is a bad idea; I find that most serious performance problems cannot be solved by those means, instead you need a higher level approach. -- Lennart On Dec 31, 2006, at 11:47 , Kirsten Chevalier wrote:
On 12/31/06, Bulat Ziganshin
wrote: this don't say anything place. and these rules have their own source: it's hard to optimize using your path. but when program optimization is just adding a few options/pragmas to the program, it' becomes cheap enough to change these rules. didn't you thought about it?
In my experience, adding pragmas and toying with options without insight into what they do is not "cheap", because it takes up the programmer's time, and time is more important than anything else. Every minute spent typing in pragmas is a minute lost that could have been spent thinking about how to write your code more elegantly, and in my experience -- and again, maybe it's just that I'm slow -- adding pragmas doesn't help. When it comes to inlining and specializing, GHC tends to be smarter than I am. (Once more, maybe it's just that I'm slow.) I'd rather focus my energies on doing the things GHC can't (usually) do, like replacing an O(n^2) algorithm with an O(log n) algorithm.
Cheers, Kirsten
-- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "Happy is all in your head / When you wake up and you're not dead / It's a sign of maturation / That you've lowered your expectations..."-- Barbara Kessler _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Hello Lennart, Sunday, December 31, 2006, 10:26:24 PM, you wrote:
I think you can be much more productive in optimizing your code if you actually understand what's going on. I usually don't go as far as looking at compiler intermediate code; I usually stick with profiling (or look at assembly code if it's a really performance critical inner loop).
for GHC??? :) if we say about optimizing C programs, old days i usually looked into asm code and then darken and go to rewrote it in asm :) nowadays i look only at memory access patterns, it's enough, at least for a few programs i optimized this year for GHC, i usually know which functions need to become faster, so i personally don't need profiling. moreover, program should be optimized for wide variety of situations, while profiling run shows only one execution scenario. i don't think that looking into STG (don't even said about C--/asm) may help me to optimize program. which program-specific things i can find here, after i've once learned how generation of STG code works?
Then you can start optimizing. That can be by changing algorithm, changing data representation, strictness annotations, etc.
yes, i don't use profiling because i use more specific instruments to investigate performance bottlenecks
It can also be by inserting some INLINE or SPECIALIZE pragmas, but that's more rare (don't get me wrong about those pragmas, I introduced them in Haskell with hbc). But I think just adding pragmas willy-nilly is a bad idea; I find that most serious performance problems cannot be solved by those means, instead you need a higher level approach.
i proposed it as fast approach to raise performance, as alternative to your suggestion to profile and read code, where you not said anything about changing algorithms :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Hello Kirsten, Sunday, December 31, 2006, 7:47:18 PM, you wrote:
this don't say anything place. and these rules have their own source: it's hard to optimize using your path. but when program optimization is just adding a few options/pragmas to the program, it' becomes cheap enough to change these rules. didn't you thought about it?
In my experience, adding pragmas and toying with options without insight into what they do is not "cheap", because it takes up the programmer's time, and time is more important than anything else.
using pragmas is much cheaper than profiling/reading low level code
Every minute spent typing in pragmas is a minute lost that could have
it's a great loss :)
been spent thinking about how to write your code more elegantly, and in my experience -- and again, maybe it's just that I'm slow -- adding pragmas doesn't help. When it comes to inlining and specializing, GHC tends to be smarter than I am. (Once more, maybe it's just that I'm slow.) I'd rather focus my energies on doing the things GHC can't (usually) do, like replacing an O(n^2) algorithm with an O(log n) algorithm.
in a minute? :) i don't agree with your arguments. if you want your program to become faster you should use various techniques. what i proposed is fastest one. i don't see meaning in opposing algorithm optimization to other techniques my own optimization experience was experimenting with variants of source code and once i understood which variants of code can't be inlined at all i just use inline pragma for all critical functions. i've learned how ghc generates STG code once and for all, and don't think that i need to see STG anymore -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 12/30/06, Bulat Ziganshin
Hello Kirsten,
Friday, December 29, 2006, 6:30:22 PM, you wrote:
I suggest *not* using these pragmas unless a combination of profiling and reading intermediate code dumps suggests that foo -- and its un-specialized nature -- is truly a bottleneck.
it's a matter of taste - and experience. may be it's simpler to add a huge number of INLINE pragmas than to profile and especially read code dumps?
I agree that profiling and reading code dumps can be daunting, but in my opinion, it's better to learn these skills once and for all (and unfortunately, these skills are still necessary given the current level of Haskell technology) and gain insight into how to use the compiler to get the code you want than to practice cargo-cult programming in the form of wanton pragmas. Cheers, Kirsten -- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt "Religion is just a fancy word for the Stockholm Syndrome." -- lj user="pure_agnostic"

Hello Kirsten, Saturday, December 30, 2006, 6:23:09 PM, you wrote:
I agree that profiling and reading code dumps can be daunting, but in my opinion, it's better to learn these skills once and for all (and unfortunately, these skills are still necessary given the current level of Haskell technology) and gain insight into how to use the compiler to get the code you want than to practice cargo-cult programming in the form of wanton pragmas.
i'm agree - you will need to try this once yourself before you will decide to do that i propose :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Chris Kuklewicz
I played with such things, as seen on the old wiki: http://haskell.org/hawiki/ShortExamples_2fSymbolDifferentiation
Cool hack! What is the use of a GADT buying in this example? Replacing the GADT with the seemingly equivalent non-GADT declaration (**) (and adding Simplify b to the context of reflectVar) seems to yield the same results. thanks -m ** data Simplify a => T a = -- Base Var SymName | Simplify a => Const a -- Num | Add [T a] | Mul [T a] | Sub (T a) (T a) | Neg (T a) | Abs (T a) | SigNum (T a) -- Fractional | Div (T a) (T a) | Recip (T a) -- Floating | Exp (T a) | Sqrt (T a) | Log (T a) | Pow (T a) (T a) | LogBase (T a) (T a) | Sin (T a) | Cos (T a)

Grady Lemoine wrote:
f x = x^3
f' = snd . (evalDeriv f)
When I load this in GHCi, I get:
*Main> :t f f :: (Num a) => a -> a *Main> :t snd . (evalDeriv f) snd . (evalDeriv f) :: (Num a, Num (Dual a)) => a -> a *Main> :t f' f' :: Integer -> Integer
Why is the type of f' Integer -> Integer, especially when the type of the expression it's defined as is more general? Is this something I'm not understanding about Haskell, or is it more to do with GHC 6.4.2 specifically?
This is the monomorphism restriction, which basically says that any binding which just has a single variable on the left of the '=' is artificially forced to be monomorphic (unless you've given it an explicit type signature) eg: f = \x -> x + 1 -- Integer -> Integer whereas f x = x + 1 -- Num a => a -> a The reason for this is that if you have something like: let x = e in (x,x) you often expect the expression (e) to be evaluated at most once, and shared by the two components of the tuple. However if there were no monomorphism restriction, then (x) could be polymorphic hence (e) would have to be evaluated twice (once for each overloading, even if the result tuple is fed to another function which expects both elements to have the same type) which would make programs run slower than the programmer expected. I couldn't find any page on the wiki about this, but there's lots of stuff scattered around on the web, and endless discussions in the Haskell mailing lists which make entertaining reading ;-) (The MR is controversial - see http://hackage.haskell.org/trac/haskell-prime/wiki/MonomorphismRestriction for the latest on what might happen to it in future versions of Haskell.) Brian. -- http://www.metamilk.com
participants (9)
-
Brandon S. Allbery KF8NH
-
Brian Hulley
-
Bulat Ziganshin
-
Chris Kuklewicz
-
Dan Piponi
-
Grady Lemoine
-
Kirsten Chevalier
-
Lennart Augustsson
-
Mike Gunter