Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
哈斯凯尔外国金融机构进出C区需要多少钱?_C_Performance_Haskell_Ffi - Fatal编程技术网

哈斯凯尔外国金融机构进出C区需要多少钱?

哈斯凯尔外国金融机构进出C区需要多少钱?,c,performance,haskell,ffi,C,Performance,Haskell,Ffi,如果我想调用多个C函数,每个函数取决于前一个函数的结果,那么创建一个包装器C函数来处理这三个调用是否更好?它的成本是否与使用Haskell FFI而不转换类型相同 假设我有以下Haskell代码: foo :: CInt -> IO CInt foo x = do a <- cfA x b <- cfB a c <- cfC c return c foo :: CInt -> IO CInt foo x = do c <- cfABC x

如果我想调用多个C函数,每个函数取决于前一个函数的结果,那么创建一个包装器C函数来处理这三个调用是否更好?它的成本是否与使用Haskell FFI而不转换类型相同

假设我有以下Haskell代码:

foo :: CInt -> IO CInt
foo x = do
  a <- cfA x
  b <- cfB a
  c <- cfC c
  return c
foo :: CInt -> IO CInt
foo x = do
  c <- cfABC x
  return c
哈斯克尔代码:

foo :: CInt -> IO CInt
foo x = do
  a <- cfA x
  b <- cfB a
  c <- cfC c
  return c
foo :: CInt -> IO CInt
foo x = do
  c <- cfABC x
  return c
foo::CInt->IO CInt
foox=do
这可能在很大程度上取决于您的Haskell编译器、c编译器以及将它们绑定在一起的胶水。唯一确定答案的方法就是测量它


从更哲学的角度讲,每次混合语言都会给新来者造成障碍:在这种情况下,仅仅流利地使用Haskell和C语言是不够的(这已经给出了一个很窄的范围),但是你还必须了解调用约定以及其他不足以使用它们的东西。很多时候都有一些微妙的问题需要处理(甚至调用C++的C语言,它们非常相似的语言并不完全是微不足道的)。除非有非常令人信服的理由,否则我会坚持使用单一语言。我能马上想到的唯一例外是创建一个预先存在的复杂库的Haskell绑定,比如Python的NumPy。

答案主要取决于外部调用是
安全的
调用还是
不安全的
调用

unsafe
C调用基本上只是一个函数调用,因此,如果没有(非平凡的)类型转换,那么如果进行三个外部调用,则会有三个函数调用,使用C编写包装器时会有一到四个函数调用,这取决于编译C时可以内联多少组件函数,因为对C的外部调用不能由GHC内联。这样的函数调用通常非常便宜(它只是参数的一个副本和到代码的一个跳转),因此无论哪种方式,差异都很小,当没有C函数可以内联到包装器中时,包装器应该稍微慢一点,当所有函数都可以内联时,包装器应该稍微快一点[在我的基准测试中确实是这样,+1.5ns resp.-3.5ns,其中三个外部调用仅返回参数就花费了大约12.7ns的时间]。如果函数做了一些不寻常的事情,那么差异可以忽略不计(如果它们没有做任何不寻常的事情,您可能最好用Haskell编写它们,让GHC内联代码)

一个
safe
C调用涉及到保存一些不寻常的状态、锁定,可能产生一个新的操作系统线程,因此需要更长的时间。那么,与外部调用的成本相比,在C中多调用一个函数的小开销可以忽略不计[除非传递参数需要非常多的复制,否则会有许多巨大的
struct
s左右]。在我的无所事事基准测试中

{-# LANGUAGE ForeignFunctionInterface #-}
module Main (main) where

import Criterion.Main
import Foreign.C.Types
import Control.Monad

foreign import ccall safe "funcs.h cfA" c_cfA :: CInt -> IO CInt
foreign import ccall safe "funcs.h cfB" c_cfB :: CInt -> IO CInt
foreign import ccall safe "funcs.h cfC" c_cfC :: CInt -> IO CInt
foreign import ccall safe "funcs.h cfABC" c_cfABC :: CInt -> IO CInt

wrap :: (CInt -> IO CInt) -> Int -> IO Int
wrap foo arg = fmap fromIntegral $ foo (fromIntegral arg)

cfabc = wrap c_cfABC

foo :: Int -> IO Int
foo = wrap (c_cfA >=> c_cfB >=> c_cfC)

main :: IO ()
main = defaultMain
            [ bench "three calls" $ foo 16
            , bench "single call" $ cfabc 16
            ]
在所有C函数都只返回参数的情况下,单个包装调用的平均值略高于100ns[105-112],三个单独调用的平均值约为300ns[290-315]


因此,一个
safe
c调用大约需要100ns,通常情况下,将它们打包成一个调用会更快。但是,如果被调用的函数做了一些非常重要的事情,差异就无关紧要了。

我一点也不确定,但我发现这很有启发性。如果我正确地解释它,
foreign-ccall不安全
(以
unsafe
为关键),基本上与内联C函数调用一样便宜。但是,在使用
unsafe
和safe变体(
foreign ccall
)时必须非常小心成本更高,而且需要锁。你会认为任何差异都会被编译掉…@Foskers:你是什么意思?@ThiagoNegri:我做了一些粗糙的(非标准)基准测试来比较
外国ccall
外国ccall unsafe
。我有一个C函数,给定
双x
返回
sin(x)*sin sin x*cos(x)/2.0
。我用GCC4.7.2和-O2编译了它。基准测试使用100000000个从0到pi/2的不同参数调用它,并对结果进行求和。使用
外来ccall
它运行约9.6秒,而
外来ccall不安全
则运行4.6秒。从实际的C程序调用它的运行时间为4.4-4.5秒。Thi至少s给了你一些想法。Haskell代码是用GHC 7.4.2编译的。@gspr:忘了我说过什么。我对外国金融机构的知识还不够。我觉得这没有抓住提问者的重点。他在问“做x需要多少性能成本?”你的答案似乎可以归结为“很难说,但不要做x,因为人们更难理解你的代码”。@gspr:不。我说唯一能确保的方法是测量他的精确设置,因为有太多的变量。但是他问性能,并且(在读了Bentley的“编写高效程序”和“编程珍珠”以及Kernighan和Pike的之后)《编程的实践》和Unix人群的其他书籍)我坚信,人的时间比计算机时间要昂贵得多,除了非常狭窄的情况。因此Knuth的格言“过早优化是万恶之源”听起来是正确的。当然,但我不认为这是不合理的假设(为了这个问题)作者得出结论,他确实需要从Haskell打电话到C,因此他来询问涉及的费用以及他应该如何安排他的电话。尽管如此,我想我的否决票有点太多了。可悲的是,它现在被锁定了。@gspr,以我的经验(我引用的文献充分证实了这一点)众所周知,人们不善于猜测真正的成本在哪里,而且许多人(几乎所有人?)倾向于在性能问题的第一个提示下就匆忙进行一些微优化探索。很可能OP确实理解这一切(在这种情况下,我希望有一个详细的解释/辩护)但是这里还有很多其他不那么开明的读者。我认为我的听众远不止少数人问、评论和回答。正如其他人所说的,Y。