在我的kdtree
项目中,我根据Int
中的Key a
类型,将深度计数器从基于a
- 基于显式KDTree v a
替换为显式benchmarking nr/kdtree_nr
mean: 60.19084 us, lb 59.87414 us, ub 60.57270 us, ci 0.950
std dev: 1.777527 us, lb 1.494657 us, ub 2.120168 us, ci 0.950
。这是diff。
现在,虽然我认为这应该是类型级别的更改,但我的基准测试显示性能急剧下降:
在:
benchmarking nr/kdtree_nr
mean: 556.9518 us, lb 554.0586 us, ub 560.6128 us, ci 0.950
std dev: 16.70620 us, lb 13.58185 us, ub 20.63450 us, ci 0.950
后:
data Key a :: *
在我深入 Core 之前......任何人都知道这里发生了什么?
正如Thomas(和userxyz)所建议的,我将type Key a :: *
替换为benchmarking nr/kdtree_nr
mean: 538.2789 us, lb 537.5128 us, ub 539.4408 us, ci 0.950
std dev: 4.745118 us, lb 3.454081 us, ub 6.969091 us, ci 0.950
并相应地更改了实现。这对结果没有任何重大影响:
lvl20 :: KDTree Vector (V3 Double) -> [V3 Double]
lvl20 =
\ (w4 :: KDTree Vector (V3 Double)) ->
$wpointsAround $fKDCompareV3_$s$fKDCompareV3 lvl2 lvl4 nrRadius q w4
快速浏览一下Core输出。显然,这种改变会阻止依赖于类的函数专门化,对吧?
在:
lvl18 :: KDTree Vector (V3 Double) -> [V3 Double]
lvl18 =
\ (w4 :: KDTree Vector (V3 Double)) ->
$wpointsAround $dKDCompare lvl1 lvl3 nrRadius q w4
后:
src/Data/KDTree.hs:48:49:
Could not deduce (k ~ KeyV3)
from the context (Real a, Floating a)
bound by the instance declaration at src/Data/KDTree.hs:45:10-49
or from (Key k)
bound by the type signature for
dimDistance :: Key k => k -> V3 a -> V3 a -> Double
at src/Data/KDTree.hs:47:3-13
‘k’ is a rigid type variable bound by
the type signature for
dimDistance :: Key k => k -> V3 a -> V3 a -> Double
at src/Data/KDTree.hs:47:3
Relevant bindings include
k :: k (bound at src/Data/KDTree.hs:47:15)
dimDistance :: k -> V3 a -> V3 a -> Double
(bound at src/Data/KDTree.hs:47:3)
In the pattern: V3X
In a case alternative: V3X -> ax - bx
In the second argument of ‘($)’, namely
‘case k of {
V3X -> ax - bx
V3Y -> ay - by
V3Z -> az - bz }’
编辑2的小更新:对 INLINE pragma 发疯并不会在这里改变一件事。
快速实施 userxyz 建议的内容:http://lpaste.net/104457 曾经去过那里,不能让它发挥作用:
{{1}}
我对这个解决方案不太满意,因为这意味着我必须放一个大的#34;请专注于你的电话以获得不错的表现"文档中的警告。
答案 0 :(得分:1)
我只是偶然发现了这个问题:Transitivity of Auto-Specialization in GHC
OP引用了“来自the docs GHC 7.6:”(强调我的):
[Y]你通常甚至不需要 SPECIALIZE pragma。在编译模块M时,GHC的优化器(带-O)自动考虑在M中声明的每个顶级重载函数,并将其专门用于在M中调用它的不同类型。优化器还会考虑每个导入的 INLINABLE 重载函数,并将其专门用于在M中调用它的不同类型。
因此,我刚刚删除了所有(硬) INLINE 和 SPECIALIZE pragma,并在适当的情况下将其替换为 INLINEABLE pragma(即on基准套件中使用的每个函数)。因此,与所有函数的内联编译指示相比,我获得了更好的时间。
Quintessence:让编译器完成它的工作,但有时会给他一个提示。
答案 1 :(得分:0)
这可能没有帮助,因为它没有解决真正的问题,即代码速度变慢,但您可以使用type
代替data
。 kFirst
和kSucc
不起作用的原因是因为无法从应用程序中推断出a
是什么,因此无法选择实例,因为实例取决于仅限a
而非Key a
。您可以通过为这些功能提供见证来解决此问题:
class KDCompare a where
type Key a :: *
kSuccWith :: proxy a -> Key a -> Key a
kFirstWith :: proxy a -> Key a
然后相应地修改你的功能:
kdtree :: (KDCompare a, G.Vector v a) => BucketSize -> v a -> KDTree v a
kdtree mb vs = ana (kdtreeF mb) (kFirstWith vs, vs)
kdtreeF :: (KDCompare a, G.Vector v a) => BucketSize -> (Key a,v a) -> KDTreeF v a (Key a,v a)
kdtreeF (BucketSize mb) (k0, v0) = go (k0, v0)
where go (k,fs) | G.length fs <= mb = LeafF (G.convert fs)
| otherwise = NodeF k (G.head r) (kSucc' k,l) (kSucc' k,r)
where (l,r) = splitBuckets k fs
kSucc' = kSuccWith v0
分开Key
和KDCompare
可能更有意义:
class Enum a => Key a where
kSucc :: a -> a
kFirst :: a
class KDCompare a where
dimDistance :: Key key => key -> a -> a -> Double
realDistance :: a -> a -> Double
然后,您的数据类型必须通过密钥进行参数化:
data KDTree k v a = Node k a (KDTree k v a) (KDTree k v a)
| Leaf (v a)
data KDTreeF k v a f = NodeF k a f f | LeafF (v a)
deriving (Functor)
但是你的功能可以更自然地写出来:
kdtree :: (Key key, KDCompare a, G.Vector v a) => BucketSize -> v a -> KDTree key v a
kdtree mb vs = ana (kdtreeF mb) (kFirst, vs)