Why is my haskell code so slow
我创建了一个用于与iron.io消息排队服务交谈的库。代码使用wreq,非常简单:
网络/ironmq/types.hs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module Network.IronMQ.Types where import Data.Aeson.TH import Data.Aeson.Types (camelTo) import Data.Text (Text) import Data.Char (toLower) data Client = Client { token :: Text, projectID :: Text, server :: Text, apiVersion :: Text } deriving (Show) data QueueSummary = QueueSummary { qsId :: Text, qsProjectId :: Text, qsName :: Text } deriving (Show) $(deriveJSON defaultOptions{fieldLabelModifier = drop 3.camelTo '_', constructorTagModifier = map toLower, omitNothingFields = True} ''QueueSummary) data Message = Message { mId :: Maybe Text, mBody :: Text, mTimeout :: Maybe Int, mReservedCount :: Maybe Int } deriving (Show) $(deriveJSON defaultOptions{fieldLabelModifier = drop 2.camelTo '_', constructorTagModifier = map toLower, omitNothingFields = True} ''Message) |
网络/ironmq.hs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | {-# LANGUAGE OverloadedStrings #-} module Network.IronMQ ( Client(..), queue, message, queues, getQueue, getMessages', getMessages, getMessageById, postMessages, clear, deleteQueue, deleteMessage, peek', peek, touch, release, update ) where import Network.Wreq import Network.Wreq.Types (Postable) import Control.Lens import Data.Aeson (FromJSON, toJSON) import Data.Map (fromList, Map) import Data.Text (Text, append, unpack, pack) import Data.Text.Encoding (encodeUtf8) import Network.IronMQ.Types import Network.HTTP.Client (RequestBody(..)) -- * Some type synonyms to help keep track of things type Endpoint = Text type Param = (Text, Text) type QueueName = Text type ID = Text -- could be a message ID, subscriber ID or whatever -- * Some functions to make HTTP requests easier -- | Construct a base URL for HTTP requests from a client baseurl :: Client -> Text baseurl client ="https://" `append` server client `append`"/" `append` apiVersion client `append`"/projects/" `append` projectID client -- | An empty body for POST/PUT requests emptyBody :: Payload emptyBody = Raw"application/json" $ RequestBodyLBS"" -- | Make a GET request to an endpoint using connection info from client and -- query string set to parameters. Return the JSON results getJSONWithOpts :: FromJSON a => Client -> Endpoint -> [Param] -> IO a getJSONWithOpts client endpoint parameters = do let url = baseurl client `append` endpoint getOpts = defaults & header"Content-Type" .~ ["application/json"] & params .~ ("oauth", token client) : parameters response <- asJSON =<< getWith getOpts (unpack url) return (response ^. responseBody) -- | Make a GET request to an endpoint using the connection info from client. -- Return the JSON results. getJSON ::FromJSON a => Client -> Endpoint -> IO a getJSON client s = getJSONWithOpts client s [] -- | Make a POST a request to an endpoint using connection info from client -- and the body provided. Return the JSON response. postJSONWithBody :: (Postable a, FromJSON b) => Client -> Endpoint -> a -> IO b postJSONWithBody client endpoint body = do let url = baseurl client `append` endpoint postOpts = defaults & header"Content-Type" .~ ["application/json"] & header"Authorization" .~ [encodeUtf8 ("OAuth" `append` token client)] response <- asJSON =<< postWith postOpts (unpack url) body return (response ^. responseBody) -- | Make a POST request to an endpoint using the connection into from client -- and an empty body. Returb the JSON response. postJSON :: (FromJSON b) => Client -> Endpoint -> IO b postJSON client endpoint = postJSONWithBody client endpoint emptyBody deleteJSON :: FromJSON a => Client ->Endpoint -> IO a deleteJSON client endpoint = do let url = baseurl client `append` endpoint deleteOpts = defaults & header"Content-Type" .~ ["application/json"] & header"Authorization" .~ [encodeUtf8 ("OAuth" `append` token client)] response <- asJSON =<< deleteWith deleteOpts (unpack url) return (response ^. responseBody) -- | Get a list of queues available to the client queues :: Client -> IO [QueueSummary] queues client = getJSON client"/queues" -- | Get a queue from the client getQueue :: Client -> QueueName -> IO Queue getQueue client queueName = getJSON client ("/queues/" `append` queueName) -- | Get a list of messages on the queue (allowing specification of number of messages and delay) getMessages' :: Client -> QueueName -> Maybe Int -> Maybe Int -> IO MessageList getMessages' client queueName max_ timeout = getJSONWithOpts client endpoint params' where endpoint ="/queues/" `append` queueName `append`"/messages" params' = case (max_, timeout) of (Nothing, Nothing) -> [] (Just x, Nothing) -> [("n", pack (show x))] (Nothing, Just y) -> [("wait", pack (show y))] (Just x, Just y) -> [("n", pack (show x)), ("wait", pack (show y))] -- | Get a list of messages on a queue getMessages :: Client -> QueueName -> IO MessageList getMessages client queueName = getMessages' client queueName Nothing Nothing -- | Get a message by ID getMessageById :: Client -> QueueName -> ID -> IO Message getMessageById client queueName messageID = getJSON client ("/queues/" `append` queueName `append`"/messages/" `append` messageID) -- | Post messages to a queue postMessages :: Client -> QueueName -> [Message] -> IO IronResponse postMessages client queueName messages_ = postJSONWithBody client endpoint body where endpoint ="/queues/" `append` queueName `append`"/messages" body = toJSON MessageList {mlMessages = messages_} -- | Delete a message from a queue deleteMessage :: Client -> QueueName -> ID -> IO IronResponse deleteMessage client queueName messageID = deleteJSON client endpoint where endpoint ="/queues/" `append` queueName `append`"/messages/" `append` messageID |
号
我正在使用wreq库运行基准测试:
长凳/基准.hs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | {-# LANGUAGE OverloadedStrings #-} import Criterion.Main import Network.IronMQ import Network.IronMQ.Types main :: IO () main = defaultMain [bench"get queue info, post a message, get messages, delete message" $ nfIO (doStuff)] testClient :: Client testClient = Client { token ="secret token", projectID ="secret project id", server ="mq-aws-us-east-1.iron.io", apiVersion ="1" } doStuff :: IO () doStuff = do _ <- queues testClient postMessages testClient"default" [message{mBody ="This is message number"}] messageList <- getMessages testClient"default" let messageID = mId (head (mlMessages messageList)) case messageID of Just x -> deleteMessage testClient"default" x return () |
现在基准测试工具告诉我代码平均运行需要1.4秒。我已经编写了一个相应的python程序,它平均需要0.10秒(最多10次重复是0.24秒)来执行相同的任务。
我是初学者Haskell程序员,所以我知道这段代码有很大的改进空间。有人能指出我怎样才能获得与haskell中的python代码相当的性能吗?
使用会话确保wreq重用跨请求的连接。请参阅多个请求以获取示例。