Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.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
Haskell 使用并发的简单csv导入_Haskell - Fatal编程技术网

Haskell 使用并发的简单csv导入

Haskell 使用并发的简单csv导入,haskell,Haskell,我试图解决这个学术用例:解析csv文件并使用多线程在数据库中插入数据 在Java中,我编写了一个解决方案,它使用主线程将数据读入集合,然后并发运行8个任务将数据插入数据库。在我的8核机器上,它在60年代完成了一个100万行csv文件2列的工作:标题和价格 然后,我尝试用我的初学者技能在haskell中编写等价的代码: {-语言重载字符串-} 导入数据.Text 将限定的Data.Text.IO作为TIO导入 导入文本.Parsec 导入Text.Parsec.Text解析器 导入Database

我试图解决这个学术用例:解析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 不幸的是,这是非常缓慢的几分钟


我想我没有正确使用异步。有人能给我一个解决方案的例子,使这个程序使用多线程有效

您的代码有问题,它会为每一行启动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