Size: a a a

2021 April 03

K

Kir in Haskell
I O
Во-первых, на main обязательно нужен -fno-cse, или разные аргументы для bar, иначе ghc сделает

let x = bar (); y = print x in y >> y

Во-вторых, нужно что-то типа такого:

ping :: forall a. Show a => a -> Int
ping x = traceShow ("ping", x) 42

{-# NOINLINE bar #-}
bar :: a -> Int
bar a = noinline ping (a `seq` ())

Само по себе noinline здесь не помогает, суть в том, что из-за него ghc не знает, будет ли вычислен аргумент ping, поэтому не может вынести seq a вперед и убрать зависимось ping от аргумента, поэтому и не выносит
Под -O1 выводит только 1 раз.

import Debug.Trace

import GHC.Magic (noinline)

ping :: Show a => a -> Int
ping x = traceShow ("ping", x) 42

{-# NOINLINE bar #-}
bar :: a -> Int
bar a = noinline ping (a `seq` ())

main = do
 print $ bar ()
 print $ bar ()
источник

K

Kir in Haskell
А, cse ещё убрать
источник

K

Kir in Haskell
https://stackoverflow.com/questions/5920200/how-to-prevent-common-sub-expression-elimination-cse-with-ghc - десять лет назад предлагали разнообразить вечер посредством case и let
источник

K

Kir in Haskell
@Elvecent https://gitlab.haskell.org/ghc/ghc/-/issues/12620 - о, там как раз кондуиты всплывали, looks related
источник

KV

Kirill Valyavin in Haskell
https://mmhaskell.com/blog/2017/5/15/untangling-haskells-strings
Всё правильно написано?
источник

L

Lierdakil in Haskell
Kir
А, cse ещё убрать
С -fno-cse можно почти совсем без магии.

{-# OPTIONS_GHC -fno-cse #-}
import Debug.Trace

import Control.Monad.ST

{-# NOINLINE bar #-}
bar :: ST s Int
bar = traceM "bar" >> pure 42

main = do
 print $ runST bar
 print $ runST bar

Как вариант можно поиграться с undefined как параметром (он каждый раз новый потому что HasCallStack):

{-# NOINLINE bar #-}
bar :: a -> ST s Int
bar _ = traceM "bar" >> pure 42

main = do
 print $ runST (bar undefined)
 print $ runST (bar undefined)

это сработает без -fno-cse потому что bar undefined != bar undefined в разных местах.
источник

K

Kir in Haskell
Lierdakil
С -fno-cse можно почти совсем без магии.

{-# OPTIONS_GHC -fno-cse #-}
import Debug.Trace

import Control.Monad.ST

{-# NOINLINE bar #-}
bar :: ST s Int
bar = traceM "bar" >> pure 42

main = do
 print $ runST bar
 print $ runST bar

Как вариант можно поиграться с undefined как параметром (он каждый раз новый потому что HasCallStack):

{-# NOINLINE bar #-}
bar :: a -> ST s Int
bar _ = traceM "bar" >> pure 42

main = do
 print $ runST (bar undefined)
 print $ runST (bar undefined)

это сработает без -fno-cse потому что bar undefined != bar undefined в разных местах.
Колдун!
источник

DK

Dmitry Kovriga in Haskell
подскажите, какая книга больше подходит для углубленного изучения hakell, haskell in depth https://www.manning.com/books/haskell-in-depth или
Haskell Design Patterns https://www.packtpub.com/product/haskell-design-patterns/9781783988723
источник

VL

Vladimir Lebed in Haskell
Эти обе хороши. Но HID я бы рекомендовал первой.
источник

OS

Oleksandr Shyshko in Haskell
альтернативно https://thinkingwithtypes.com/
источник

KV

Kirill Valyavin in Haskell
Алтернативно Haskell High Performance Programming, вот там реально глубь, а не ширь
источник

IO

I O in Haskell
Lierdakil
С -fno-cse можно почти совсем без магии.

{-# OPTIONS_GHC -fno-cse #-}
import Debug.Trace

import Control.Monad.ST

{-# NOINLINE bar #-}
bar :: ST s Int
bar = traceM "bar" >> pure 42

main = do
 print $ runST bar
 print $ runST bar

Как вариант можно поиграться с undefined как параметром (он каждый раз новый потому что HasCallStack):

{-# NOINLINE bar #-}
bar :: a -> ST s Int
bar _ = traceM "bar" >> pure 42

main = do
 print $ runST (bar undefined)
 print $ runST (bar undefined)

это сработает без -fno-cse потому что bar undefined != bar undefined в разных местах.
У меня такое чувство что это не совсем то, что вы хотите. Сам трейс конечно никуда не выносится, у него сайдэффекты, но на значение, переданное в pure, это не распостраняется. Поэтому такое


bar :: ST s [Int]
bar = traceM "bar" >> pure [1..1000]
{-# NOINLINE bar #-}

прекрасно компилируется в такое

lvl_r1yD :: [Int]
lvl_r1yD = eftInt 1# 1000#

-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl1_r1AX :: Addr#
lvl1_r1AX = "bar"#

lvl2_r1AY :: [Char]
lvl2_r1AY = unpackCString# lvl1_r1AX

lvl3_r1AZ :: forall s. State# s -> (# State# s, () #)
lvl3_r1AZ
 = \ (@ s_a1kz) (s1_a1zH :: State# s_a1kz) -> (# s1_a1zH, () #)

bar1_r1B0 :: forall s. State# s -> (# State# s, [Int] #)
bar1_r1B0
 = \ (@ s_a1kz) (s1_X1zo :: State# s_a1kz) ->
     case ((trace lvl2_r1AY (lvl3_r1AZ cast <Co:4>)) cast <Co:3>)
            s1_X1zo
     of
     { (# ipv_a1zn, ipv1_a1zo #) ->
     (# ipv_a1zn, lvl_r1yD #)
     }

bar :: forall s. ST s [Int]
bar = bar1_r1B0 cast <Co:8>

[1..1000] опять оказался на топ левеле, чего мы вроде не хотели
источник

L

Lierdakil in Haskell
I O
У меня такое чувство что это не совсем то, что вы хотите. Сам трейс конечно никуда не выносится, у него сайдэффекты, но на значение, переданное в pure, это не распостраняется. Поэтому такое


bar :: ST s [Int]
bar = traceM "bar" >> pure [1..1000]
{-# NOINLINE bar #-}

прекрасно компилируется в такое

lvl_r1yD :: [Int]
lvl_r1yD = eftInt 1# 1000#

-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl1_r1AX :: Addr#
lvl1_r1AX = "bar"#

lvl2_r1AY :: [Char]
lvl2_r1AY = unpackCString# lvl1_r1AX

lvl3_r1AZ :: forall s. State# s -> (# State# s, () #)
lvl3_r1AZ
 = \ (@ s_a1kz) (s1_a1zH :: State# s_a1kz) -> (# s1_a1zH, () #)

bar1_r1B0 :: forall s. State# s -> (# State# s, [Int] #)
bar1_r1B0
 = \ (@ s_a1kz) (s1_X1zo :: State# s_a1kz) ->
     case ((trace lvl2_r1AY (lvl3_r1AZ cast <Co:4>)) cast <Co:3>)
            s1_X1zo
     of
     { (# ipv_a1zn, ipv1_a1zo #) ->
     (# ipv_a1zn, lvl_r1yD #)
     }

bar :: forall s. ST s [Int]
bar = bar1_r1B0 cast <Co:8>

[1..1000] опять оказался на топ левеле, чего мы вроде не хотели
Да, всё верно, чистые значения из-под ST прекрасно выползут. Но если вычисление списка завернуть в ST то уже не выползут:
bar = traverse pure [trace "bar" 1..1000]
выведет bar дважды. Ну или  через STT [] должно быть то же самое (но я не проверял)
источник

L

Lierdakil in Haskell
Lierdakil
Да, всё верно, чистые значения из-под ST прекрасно выползут. Но если вычисление списка завернуть в ST то уже не выползут:
bar = traverse pure [trace "bar" 1..1000]
выведет bar дважды. Ну или  через STT [] должно быть то же самое (но я не проверял)
Это конечно если оптимизатор не сделает вид что он умнее всех и не заменит traverse pure на pure (потому что натуральность Traversable). Но пока вроде не заменяет.
источник

IO

I O in Haskell
traverse pure -> pure конечно не сделает, фьюжина для traverse нет. Зато интересно как лишний traverse скажится на призводительность.

Если само чистое значение менять можно, можно проще сделать, такого достаточно

opaqueId :: a -> a
opaqueId x = x
{-# NOINLINE opaqueId #-}

bar :: [Int]
bar = [opaqueId 1..1000]



bar1 :: Int
bar1 = I# 1#

bar :: [Int]
bar = case opaqueId bar1 of { I# x_a1xs -> eftInt x_a1xs 1000# }
источник

L

Lierdakil in Haskell
I O
traverse pure -> pure конечно не сделает, фьюжина для traverse нет. Зато интересно как лишний traverse скажится на призводительность.

Если само чистое значение менять можно, можно проще сделать, такого достаточно

opaqueId :: a -> a
opaqueId x = x
{-# NOINLINE opaqueId #-}

bar :: [Int]
bar = [opaqueId 1..1000]



bar1 :: Int
bar1 = I# 1#

bar :: [Int]
bar = case opaqueId bar1 of { I# x_a1xs -> eftInt x_a1xs 1000# }
Только на самом деле недостаточно, потому что bar- то будет шариться.
источник

[

[BRM]White Rabbit in Haskell
Читаю про rpar|rseq, первый как будто объявление таски в C#, а второй - await таски. По крайней мере внешняя работа точь в точь
источник

L

Lierdakil in Haskell
[BRM]White Rabbit
Читаю про rpar|rseq, первый как будто объявление таски в C#, а второй - await таски. По крайней мере внешняя работа точь в точь
Ээээ... нет? Я не большой специалист по parallel, но rseq это seq x (return x), т.е. он сразу на месте не отходя от кассы форсирует аргумент к WHNF. Казалось бы при чём тут await.
источник

[

[BRM]White Rabbit in Haskell
await тоже ожидает значение. не давая коду идти дальше
источник

[

[BRM]White Rabbit in Haskell
у кода
var task = Task.Delay(10);
await Task.Delay(5)
await task;


поведение абсолютно аналогично к
 x <- rpar $ f a
y <- rseq $ f b
rseq x
return (x,y)
источник