最近,诸如计算Hashmap的大小之类的博客条目解释了如何推断常用容器类型的空间复杂性.现在我面临的问题是如何实际"看到"我的GHC版本选择的内存布局(取决于编译标志和目标体系结构),用于奇怪的数据类型(构造函数),例如
data BitVec257 = BitVec257 {-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Bool
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
data BitVec514 = BitVec514 {-# UNPACK #-} !BitVec257
{-# UNPACK #-} !BitVec257
Run Code Online (Sandbox Code Playgroud)
在C中有sizeof和offsetof运算符,它允许我"看到"为C字段选择的大小和对齐方式struct.
我试着看看GHC Core希望在那里找到一些提示,但我不知道该找什么.有人能指出我正确的方向吗?
假设我创建了一个类型如下:
data RequestAck =
RequestAck { ackOK :: Word32, ackMsgCode :: Word32 }
Run Code Online (Sandbox Code Playgroud)
我可以看到它是2*4字节大,并使其在某处保持不变.
唯一的问题是,一旦我在类型中添加了一个字段,我就必须记得更新我的常量.
是否有一个功能可以为我提供给定类型的大小,例如t -> Int?
接近我想要的功能是
gsize :: Data a => a -> Int
在Data.Generics.Schemes模块中,但我不想让我的类型成为一个实例Data.
那里有更通用的解决方案吗?
可以肯定的是,我正在寻找一个对静态类型进行操作的函数,例如,我不想传递实例,而是传递类型本身.
据说 Haskell元组只是代数数据类型的不同语法.类似地,有一些示例说明如何使用元组重新定义值构造函数.
例如,Haskell中的Tree数据类型可能被写为
data Tree a = EmptyTree | Node a (Tree a) (Tree a)
Run Code Online (Sandbox Code Playgroud)
可以像这样转换为"元组形式":
data Tree a = EmptyTree | Node (a, Tree a, Tree a)
Run Code Online (Sandbox Code Playgroud)
Node第一个示例中的值构造函数tuple与第二个示例中的值构造函数有什么区别?ie Node a (Tree a) (Tree a)vs. (a, Tree a, Tree a)(除了语法之外)?
在引擎盖下,Node a (Tree a) (Tree a)每个位置的3元组适当类型只是一种不同的语法?
我知道你可以部分应用一个值构造函数,例如Node 5它将具有类型:(Node 5) :: Num a => Tree a -> Tree a -> Tree a
您也可以使用(,,)函数来部分应用元组...但是这不知道未绑定条目的潜在类型,例如:
Prelude> :t (,,) 5
(,,) 5 …Run Code Online (Sandbox Code Playgroud) 据我所知,Haskell只有当某些东西超出范围时才会收集垃圾,因此顶级绑定只会被评估一次并且永远不会超出范围.因此,如果我在GHCI中运行此代码,将评估并保存前50个元素.
let xs = map f [0..]
take 50 xs
Run Code Online (Sandbox Code Playgroud)
我的问题是当我执行以下代码段时会发生什么:xs !! 99.垃圾收集器保存了什么?可以
我的理解是,没有字段的类型的构造函数是"静态分配"的,GHC 在所有用途之间共享这些构造函数,并且GC不会移动它们.
如果这是正确的,那么我希望使用reallyUnsafePtrEquality#on值False和Nothing非常安全(没有错误否定或肯定),因为它们只能表示为与该构造函数的单个实例相同的指针.
我的推理是否正确?是否有任何潜在的问题或理由怀疑在不久的将来版本的GHC中这可能会变得不安全?
我的理解是Int值是指向thunk(双重间接)的指针,而未装箱的Int#只是指向32/64位int的指针.那是对的吗?指针如何编码它指的是未装箱的值?
所述的Haskell标准规定,一个Int是"A固定精度整数类型至少与所述范围[-2 ^ 29 .. 2 ^ 29-1]".在GHC中是否有一些优化,其中那些额外的位用于消除间接?
考虑使用存在主义的以下数据模型:
data Node a = Node a (Map TypeRep AnyNode)
data AnyNode = forall a. Show a => AnyNode a
Run Code Online (Sandbox Code Playgroud)
先前已经解释了关于标准类型的存储器占用的规则.现在,存在类型的规则是什么,比如AnyNode?
是否有任何优化技术,例如一些使用unsafeCoerce可以避开存在性声明的变通方法?我问这个是因为类似的类型Node将被放置在高内存密集型lib的成本中心,所以内存占用就足够了,这就是为什么最脏的黑客是受欢迎的.
可能重复:
Haskell数据类型的内存占用量
在解决组合问题时,我经常将解决方案表示为一个字符串,例如.1010100010110111000110 ...你明白了.
我认为当我使用[Int]比特串时,Int总是花费相同数量的内存,无论实际数量有多大(因为Int它是有界的,相反Integer),因为计算机只记得比特表示,而且String会据我所知,占据更多空间.
我的想法是使用数据类型
data Bits = Empty | Zero Bits | One Bits deriving (Eq,Ord,Show)
Run Code Online (Sandbox Code Playgroud)
但多少内存的构造Empty,Zero并One用比较Int的?
我正在研究优化Haskell代码中给出的答案,并注意到与Python相比,使用小输入确实会导致更快的Haskell运行.
但随着数据集规模的扩大,Python占据了主导地位.使用基于散列映射的版本已经改善了性能,但它仍然落后.
更糟糕的是,我尝试将Python的词典音译为哈希表并观察到性能受到严重打击.我真的想了解发生了什么,因为我需要可变结构用于未来的应用程序.
这是稍微修改过的Python代码:
#! /usr/bin/env python2.7
import random
import re
import cPickle
class Markov:
def __init__(self, filenames):
self.filenames = filenames
self.cache = self.train(self.readfiles())
picklefd = open("dump", "w")
cPickle.dump(self.cache, picklefd)
print "Built a db of length "+str(len(self.cache))
picklefd.close()
def train(self, text):
splitted = text.split(' ')
print "Total of %d splitted words" % (len(splitted))
cache = {}
for i in xrange(len(splitted)-2):
pair = (splitted[i], splitted[i+1])
followup = splitted[i+2]
if pair in cache:
if followup not in cache[pair]:
cache[pair][followup] = …Run Code Online (Sandbox Code Playgroud) Haskell中的`data`和`newtype`之间的区别以及其他一些问题解决了数据和newtype之间的一般差异.我的问题非常具体.如果G是某种类型,是否有任何区别
data T = T !G
和
newtype T = T G?
它们似乎具有相同的严格属性,我不明白为什么编译器有任何理由以不同的方式编译它们,但也许我错过了一些东西.
我有一个相对简单的"复制"程序,它只是将一个文件的所有行复制到另一个文件.我玩弄Haskell的并发支持TMQueue和STM所以我想我会尝试这样的:
{-# LANGUAGE BangPatterns #-}
module Main where
import Control.Applicative
import Control.Concurrent.Async -- from async
import Control.Concurrent.Chan
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMQueue -- from stm-chans
import Control.Monad (replicateM, forM_, forever, unless)
import qualified Data.ByteString.Char8 as B
import Data.Function (fix)
import Data.Maybe (catMaybes, maybe)
import System.IO (withFile, IOMode(..), hPutStrLn, hGetLine)
import System.IO.Error (catchIOError)
input = "data.dat"
output = "out.dat"
batch = 100 :: Int
consumer :: TMQueue B.ByteString -> IO ()
consumer q = withFile output WriteMode …Run Code Online (Sandbox Code Playgroud) 冈崎使用(基本上)
data Color = R | B
data RB a = L | T {-# UNPACK #-}!Color !(RB a) !a !(RB a)
Run Code Online (Sandbox Code Playgroud)
我知道在C中,颜色通常以更加繁琐的方式处理以节省空间,做一些事情,比如使指针的低位代表颜色(我想通常指向节点的指针会编码它的颜色,但它也会可以通过使左或右指针来模拟Okasaki的结构从一个节点代表它颜色).
显然,在Haskell中这种小小的变化是不可能的.那么,如何在Haskell中最有效地表示节点?
data RB' a = L | B !(RB a) !a !(RB a) | R !(RB a) !a !(RB a)
Run Code Online (Sandbox Code Playgroud)
似乎可能是合理的内存效率,但它似乎也可能使模式匹配相当冗长.
我用自定义文件编写了一个解析器attoparsec.分析报告表明,大约67%的内存分配是在一个名为的函数中完成的tab,这也耗费了大部分时间.该tab功能是非常简单的:
tab :: Parser Char
tab = char '\t'
Run Code Online (Sandbox Code Playgroud)
整个分析报告如下:
ASnapshotParser +RTS -p -h -RTS
total time = 37.88 secs (37882 ticks @ 1000 us, 1 processor)
total alloc = 54,255,105,384 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
tab Main 83.1 67.7
main Main 6.4 4.2
readTextDevice Data.Text.IO.Internal 5.5 24.0
snapshotParser Main 4.7 4.0
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 75 0 0.0 0.0 100.0 …Run Code Online (Sandbox Code Playgroud) haskell ×13
ghc ×4
memory ×2
performance ×2
attoparsec ×1
byte ×1
hashtable ×1
mutable ×1
optimization ×1
queue ×1
tuples ×1
types ×1