Haskell 使用并发的简单csv导入
我试图解决这个学术用例:解析csv文件并使用多线程在数据库中插入数据 在Java中,我编写了一个解决方案,它使用主线程将数据读入集合,然后并发运行8个任务将数据插入数据库。在我的8核机器上,它在60年代完成了一个100万行csv文件2列的工作:标题和价格 然后,我尝试用我的初学者技能在haskell中编写等价的代码: {-语言重载字符串-} 导入数据.Text 将限定的Data.Text.IO作为TIO导入 导入文本.Parsec 导入Text.Parsec.Text解析器 导入Database.PostgreSQL.Simple 导入数据.Int 导入控制.Concurrent.Async 行::解析器文本,文本 line=do 标题错误[文本,文本] parseCsv=解析文件 parseCsvF::FilePath->IO或ParseError[文本,文本] parseCsvF path=fmap parseCsv path$TIO.readFile path connectDB::IO连接 connectDB=connect-ConnectInfo{connectHost=localhost,connectPort=5432,connectUser=parser,connectPassword=parser,connectDatabase=parser} insertComic::连接->文本,文本->IO Int64 insertComic连接标题,价格=执行连接插入漫画标题,价格值?,?[开箱标题,开箱价格] main=do 连接>=\v->等待v 不幸的是,这是非常缓慢的几分钟Haskell 使用并发的简单csv导入,haskell,Haskell,我试图解决这个学术用例:解析csv文件并使用多线程在数据库中插入数据 在Java中,我编写了一个解决方案,它使用主线程将数据读入集合,然后并发运行8个任务将数据插入数据库。在我的8核机器上,它在60年代完成了一个100万行csv文件2列的工作:标题和价格 然后,我尝试用我的初学者技能在haskell中编写等价的代码: {-语言重载字符串-} 导入数据.Text 将限定的Data.Text.IO作为TIO导入 导入文本.Parsec 导入Text.Parsec.Text解析器 导入Database
我想我没有正确使用异步。有人能给我一个解决方案的例子,使这个程序使用多线程有效 您的代码有问题,它会为每一行启动db事务 我建议您将数据分割成块,并在一个事务中处理整个块 如果在一条insert语句中插入多条记录,也会有所帮助 编辑 另一个最大的问题是您只使用一个连接,这使得您的代码有效地按顺序进行,而不是并行进行
在处理数据之前,您还将所有数据读入内存。您也可以在这里提高性能。我在这里提出了一个解决方案,可能不是最好的解决方案,但它很容易获得比java更好的性能,代码行数要少得多,而且不使用特定的多线程机制。 我使用资源池和10000行块进行插入
{-# LANGUAGE OverloadedStrings #-}
import Data.Text
import qualified Data.Text.IO as TIO
import Text.Parsec
import Text.Parsec.Text (Parser)
import Database.PostgreSQL.Simple
import Data.Int
import Control.Concurrent.Async
import Data.Pool
import qualified Data.List.Split as Split
import System.CPUTime
line :: Parser (Text,Text)
line = do
title <- many $ noneOf ","
oneOf ","
price <- many $ digit
return (pack title,pack price)
file :: Parser [(Text,Text)]
file = line `endBy` newline
parseCsv :: SourceName -> Text -> Either ParseError [(Text,Text)]
parseCsv = parse file
parseCsvF :: FilePath -> IO (Either ParseError [(Text,Text)])
parseCsvF path = fmap (parseCsv path) $ TIO.readFile path
connectionInfo :: ConnectInfo
connectionInfo = ConnectInfo {
connectHost="localhost",
connectPort=5432,
connectUser="parser",
connectPassword="parser",
connectDatabase="parser"}
myPool :: IO (Pool Connection)
myPool = createPool (connect connectionInfo) close 1 10 10
insertComic :: Pool Connection -> [(Text , Text)] -> IO Int64
insertComic pool comic = withResource pool (\conn -> insertComic' conn comic)
insertComic' :: Connection -> [(Text,Text)] -> IO Int64
insertComic' conn comics = executeMany conn "INSERT INTO comics (title, price) VALUES (?,?)" comics
main = do
start <- getCPUTime
pool <- myPool
input <- parseCsvF "data.csv"
let (Right allComics) = input
chunks = Split.chunksOf 10000 allComics
inserts = [ insertComic pool chunk | chunk <- chunks]
sequence inserts
end <- getCPUTime
putStrLn $ show $ fromIntegral (end-start) / 10^12
请删除不相关的代码。您的java代码不会帮助任何人检查您的haskell代码。您如何使用Control.Concurrent.Async?您应该指出在使用该库时所做的更改。简单地添加导入不会添加多线程,我们需要知道您做了哪些更改。这个任务听起来应该是内存带宽受限的,而不是CPU受限的。出于好奇,在保持缓存不变的情况下,并行化在多大程度上加快了Java实现的速度done@BjarturThorlacius在没有并行化的java中:292s对64s,有4,5个因子。谢谢,我编辑了我的问题,多亏了你给我的提示。在这种情况下,你不应该编辑问题。如果你想发布你的解决方案,只需将其作为单独的答案发布即可。
{-# LANGUAGE OverloadedStrings #-}
import Data.Text
import qualified Data.Text.IO as TIO
import Text.Parsec
import Text.Parsec.Text (Parser)
import Database.PostgreSQL.Simple
import Data.Int
import Control.Concurrent.Async
import Data.Pool
import qualified Data.List.Split as Split
import System.CPUTime
line :: Parser (Text,Text)
line = do
title <- many $ noneOf ","
oneOf ","
price <- many $ digit
return (pack title,pack price)
file :: Parser [(Text,Text)]
file = line `endBy` newline
parseCsv :: SourceName -> Text -> Either ParseError [(Text,Text)]
parseCsv = parse file
parseCsvF :: FilePath -> IO (Either ParseError [(Text,Text)])
parseCsvF path = fmap (parseCsv path) $ TIO.readFile path
connectionInfo :: ConnectInfo
connectionInfo = ConnectInfo {
connectHost="localhost",
connectPort=5432,
connectUser="parser",
connectPassword="parser",
connectDatabase="parser"}
myPool :: IO (Pool Connection)
myPool = createPool (connect connectionInfo) close 1 10 10
insertComic :: Pool Connection -> [(Text , Text)] -> IO Int64
insertComic pool comic = withResource pool (\conn -> insertComic' conn comic)
insertComic' :: Connection -> [(Text,Text)] -> IO Int64
insertComic' conn comics = executeMany conn "INSERT INTO comics (title, price) VALUES (?,?)" comics
main = do
start <- getCPUTime
pool <- myPool
input <- parseCsvF "data.csv"
let (Right allComics) = input
chunks = Split.chunksOf 10000 allComics
inserts = [ insertComic pool chunk | chunk <- chunks]
sequence inserts
end <- getCPUTime
putStrLn $ show $ fromIntegral (end-start) / 10^12
ghc -threaded injector.hs -o injector
./injector +RTS -N8