gpt4 book ai didi

haskell - Haskell 和流融合不断增加 CPU 消耗

转载 作者:行者123 更新时间:2023-12-02 10:45:44 31 4
gpt4 key购买 nike

这是一个简短的 Haskell 程序,可以生成 440 Hz 的声音。它使用pulseaudio作为音频后端。

import GHC.Float
import Control.Arrow
import Sound.Pulse.Simple
import qualified Data.List.Stream as S
import Data.List

type Time = Double
type Frequency = Double
type Sample = Double
type CV = Double

chunksize = 441 * 2
sampleRate :: (Fractional a) => a
sampleRate = 44100

integral :: [Double] -> [Double]
integral = scanl1 (\acc x -> acc + x / sampleRate)

chunks :: Int -> [a] -> [[a]]
chunks n = S.takeWhile (not . S.null) . S.unfoldr (Just . S.splitAt n)

pulseaudioOutput :: [Sample] -> IO ()
pulseaudioOutput sx = do

pa <- simpleNew Nothing "Synths" Play Nothing "Synths PCM output"
(SampleSpec (F32 LittleEndian) 44100 1) Nothing Nothing

mapM_ (simpleWrite pa . S.map double2Float) $ chunks 1000 sx

simpleDrain pa
simpleFree pa

oscSine :: Frequency -> [CV] -> [Sample]
oscSine f = S.map sin <<< integral <<< S.map ((2 * pi * f *) . (2**))

music ::[Sample]
music = oscSine 440 (S.repeat 0)

main = do
pulseaudioOutput music

如果我编译并运行它,我会看到 CPU 消耗不断增长。

如果我将“ block ”定义中的“S.splitAt”更改为“splitAt”,一切都很好。

有人能猜出为什么会这样吗?

谢谢。

更新

在以下代码中,所有三个版本的 block 都可以产生上述行为:

import GHC.Float
import Control.Arrow
import Sound.Pulse.Simple
import Data.List.Stream

import Prelude hiding ( unfoldr
, map
, null
, scanl1
, takeWhile
, repeat
, splitAt
, drop
, take
)

type Time = Double
type Frequency = Double
type Sample = Double
type CV = Double

chunksize = 441 * 2
sampleRate :: (Fractional a) => a
sampleRate = 44100

integral :: [Double] -> [Double]
integral = scanl1 (\acc x -> acc + x / sampleRate)

chunks :: Int -> [a] -> [[a]]
--chunks n = takeWhile (not . null) . unfoldr (Just . splitAt n)
--chunks n xs = take n xs : chunks n (drop n xs)
chunks n xs = h : chunks n t
where
(h, t) = splitAt n xs

pulseaudioOutput :: [Sample] -> IO ()
pulseaudioOutput sx = do

pa <- simpleNew Nothing "Synths" Play Nothing "Synths PCM output"
(SampleSpec (F32 LittleEndian) 44100 1) Nothing Nothing

mapM_ (simpleWrite pa . map double2Float) $ chunks 1000 sx

simpleDrain pa
simpleFree pa

oscSine :: Frequency -> [CV] -> [Sample]
oscSine f = map sin <<< integral <<< map ((2 * pi * f *) . (2**))

music ::[Sample]
music = oscSine 440 (repeat 0)

main = do
pulseaudioOutput music

我清理了代码以避免混合普通的旧列表和流融合列表。内存/CPU 泄漏仍然存在。要查看代码是否适用于旧列表,只需删除“Data.List”后面的 Prelude 导入和“.Stream”即可。

最佳答案

被融合规则 ( http://hackage.haskell.org/package/stream-fusion-0.1.2.5/docs/Data-Stream.html#g:12 ) 替换的流上的 splitAt 具有以下签名:

splitAt :: Int -> Stream a -> ([a], [a])

从中我们可以看到,由于它生成列表而不是流,这阻碍了进一步的融合。我认为正确的做法是生成一个生成流的 splitAt ,或者更好的是直接在流上编写一个带有适当融合规则的 block 函数列表版本。

这是一个关于流的splitAt,我认为应该很好。当然,您需要将其与列表上 splitAt 中的适当重写规则配对,如果这些重写规则变得棘手,也许可以直接编写 chunks 函数,尽管它这样做似乎也有点棘手:

splitAt :: Int -> Stream a -> (Stream a, Stream a)
splitAt n0 (Stream next s0)
| n0 < 0 = (nilStream, (Stream next s0))
| otherwise = loop_splitAt n0 s0
where
nilStream = Stream (const Done) s0
loop_splitAt 0 !s = (nilStream, (Stream next s))
loop_splitAt !n !s = case next s of
Done -> (nilStream, nilStream)
Skip s' -> loop_splitAt n s'
Yield x s' -> (cons x xs', xs'')
where
(xs', xs'') = loop_splitAt (n-1) s'

关于haskell - Haskell 和流融合不断增加 CPU 消耗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22331080/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com