Форум программистов, компьютерный форум, киберфорум
Наши страницы
Haskell
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.50/4: Рейтинг темы: голосов - 4, средняя оценка - 4.50
tstusreg
4 / 4 / 0
Регистрация: 04.08.2017
Сообщений: 6
1

Haskell vs python IO

04.08.2017, 14:40. Просмотров 743. Ответов 13

Добрый день, у меня вопрос по поводу того, как улучшить хаскелл код, чтобы он работал хотя бы сопоставимо с питоном.
Задача такова: есть кучка файлов (каждый может быть до 100 Мб), которые имеют одно имя и лежат в разных директориях (например ./test/t30-1/p1.res, ./test/t30-2/p1.res, ...). Каждый файл содержит матрицу, количество столбцов всегда одинаково, строк может быть разное количество, все разделено пробелами. Матрицы собираются в памяти, после чего поэлементно складываются и каждый элемент итоговой матрицы делится на количество матриц, короче, это простое усреднение всех файлов, по файлу имеющему наименьшее количество строк. Я наивно подумал, что код переписанный в лоб с питона будет работать, по крайней мере, не медленнее, поскольку в обоих случаях используются стандартные списки, но получил облом и замедление почти в 10 раз кода на хаскеле, кто виноват, что делать?

Haskell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
module Main where
 
import Data.List
import System.Environment
import System.Directory
import System.FilePath.Posix
import qualified Data.Text as T
import qualified Data.Text.IO as T
 
type TRow = [Double]
type FMatrix = [TRow]
 
addRow :: TRow -> TRow -> TRow
addRow a b = map (\(x, y) -> x+y) $ zip a b
 
addMatrix :: FMatrix -> FMatrix -> FMatrix
addMatrix u v = zipWith addRow u v
 
divRowConst :: TRow -> Double -> TRow
divRowConst r a = map (\v -> v/a) r
 
divMatrixConst :: FMatrix -> Double -> FMatrix
divMatrixConst m a = map (\row -> divRowConst row a) m
 
makeRow :: String -> TRow
makeRow = map read . words
 
makeMatrix :: [String] -> FMatrix
makeMatrix = map makeRow
 
readMFile :: FilePath -> IO FMatrix
readMFile f = do
  content <- T.readFile f
  return (makeMatrix $ lines $ T.unpack content)  
  
toStrRow :: TRow -> String
toStrRow = unwords . map show
 
main :: IO ()
main = do
  args <- getArgs
  let path = args !! 1
  let filename = args !! 0
  let partialPath = init $ splitPath path
  let lastPattern = last $ splitPath path
  
  allDirs <- getDirectoryContents $ concat partialPath
  let filteredFiles = map (\dir -> (concat partialPath) ++ dir ++"/"++ filename ) $ filter (isPrefixOf lastPattern) allDirs
  allMatrix <- mapM readMFile filteredFiles
  T.writeFile filename $ T.pack $ unlines $ map toStrRow $ (\m -> divMatrixConst m (fromIntegral $ length allMatrix)) $ foldl1' addMatrix allMatrix
Результат профайлера в prof.txt

Питоновский "аналог"
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/python
import sys
import glob
 
def text2list(full):
    m = []
    for i in full:
        m.append([float(y) for y in i.split(" ") if y != '\n'])
    return m
 
class FMatrix(object):
 
    def __init__(self, l):
        self.m = l
        self.cols = len(l)
        self.rows = len(l[0])
        
    def __repr__(self):
        return "FMatrix, cols = {}, rows = {}, m[0][0]={}".format(self.cols, self.rows, self.m[0][0])
 
    def __str__(self):
        s = ""
        for l in self.m:
            s+=" ".join(map(str,l))+"\n"
        # return "str FMatrix, cols = {}, rows = {}, m[1][1]={}".format(self.cols, self.rows, self.m[1][1])
        return s
 
    def __add__(self, other):
        new = []
        if not isinstance(other, FMatrix):
            return NotImplemented
        for i in range(min(len(self.m), len(other.m))):
            new.append(map(lambda (x,y): x+y, zip(self.m[i], other.m[i])))
        return FMatrix(new)
    
    def __div__(self, other):
        new = []
        for i in self.m:
            new.append(map(lambda x: x/other, i))
        return FMatrix(new)
 
def usage():
    print "\tUsage:"
    print "\t./meanmat <inddir> <fname> <outdir>"
    
if __name__ == "__main__":
 
    if len(sys.argv) < 4:
        usage()
        exit(0)
        
    fpat = sys.argv[1]
    fname = sys.argv[2]
    fsave = sys.argv[3]
    folders= (glob.glob(fpat+"*"))
    a = []
    for folder in folders:
        with open(folder+"/"+fname, "r") as f:
            full = f.readlines()
            mat = FMatrix(text2list(full))
            a.append(mat)
    x = a[0]
    for xx in a[1:]:
        x=x+xx
    yy=x/len(a)
    fout = fname
    print "File {} processing done. Task {}.".format(fname, fpat.split("/")[-1])
    with open(fsave+"/"+fout, "w") as out:
        out.write(str(yy))


Для теста сделал директорию с файлами, можно посмотреть в прикрепленном файле

запускать все это безобразие можно так:
Bash
1
2
3
4
5
для питона
time python ./mean2.py ./test/t30 p1.res ./
 
для хаскела
time ./mean2-exe p1.res ./test/t30
должен получится в текущей директории файл p1.res
0
Вложения
Тип файла: 7z test.tar.7z (712.4 Кб, 2 просмотров)
Тип файла: txt prof.txt (13.9 Кб, 4 просмотров)
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
04.08.2017, 14:40
Ответы с готовыми решениями:

Место ФП и Haskell в компьютерной индустрии (Для чего он нужен, этот Haskell?)
&quot;У нас&quot; ? А где преподавание этой экзотики на высоте? Добавлено через 2 минуты А где такие...

Простой код на haskell (элементарное) - не знаю как это в python реализовать
adjectives = nouns = funnn = ввожу в консоли funnn и вот output: *F_world&gt; funnn

Python - момент истины. Python - как оружие возмездие против системы
Какие модули в python мне нужны для взлома баз данных? Перехвата информации? Внедрения в систему? ...

Cx_freeze python error in main script как исправить- Python
Пытался создать из .py .exe , но при запуске .exe получаю ошибку вот код setup.py from cx_Freeze...

Как из Python скрипта выполнить другой python скрипт?
Как из Python скрипта выполнить другой python скрипт? Если он находится в той же папке но нужно...

13
Curry
2993 / 2074 / 257
Регистрация: 01.06.2013
Сообщений: 4,527
Записей в блоге: 9
04.08.2017, 15:21 2
Цитата Сообщение от tstusreg Посмотреть сообщение
в обоих случаях используются стандартные списки
То что называется стандартными списками в разных языках может быть очень разным.
В Haskell это односвязанные списки, к тому же в "коробочках" (boxed) для поддержки лени. Делать из них матрицы не стоит, нужно использовать другие контейнерные типы. Можно взять одномерный вектор, написать несколько функций для доступа к вектору как к матрице.
Есть пакеты именно матричные, вот, к примеру, там матрица из текстового файла читается, вроде бы как вам надо.
Но я не пользовался "матричными" пакетами. Попробуйте, потом нам расскажите.

p.s. Предвидя возражения "а в Python-e это уже встроено". Я не знаю питон, но слышал что там идеология - по максимуму всё встроено в язык. Я не думаю что это хорошо, но это моё мнение и здесь не http://www.cyberforum.ru/holywars/ .
В общем, на Haskell нужно подключать пакеты.

Добавлено через 14 минут
Цитата Сообщение от tstusreg Посмотреть сообщение
Матрицы собираются в памяти, после чего поэлементно складываются и каждый элемент итоговой матрицы делится на количество матриц, короче, это простое усреднение
Зачем матрицы в памяти накапливать. Можно суммировать по мере чтения из файлов и подсчитывать кол-во загруженных матриц, в конце поделить.
3
tstusreg
4 / 4 / 0
Регистрация: 04.08.2017
Сообщений: 6
04.08.2017, 16:34  [ТС] 3
Благодарю за ответ и ссылки на пакеты, согласен, что имя топика носит оттенок потенциального холивара, заранее извиняюсь за то, что ввел в заблуждение.

Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
Зачем матрицы в памяти накапливать. Можно суммировать по мере чтения из файлов и подсчитывать кол-во загруженных матриц, в конце поделить.
Просто решение на питоне было прототипным и первоначально не было известно какой размер будет у матриц, однако по производительности оно почти полностью удовлетворяет, от прямого переписывания хотелось дешевого хаскельного чуда, а чудо не произошло, теперь надо будет копать дальше
0
Curry
2993 / 2074 / 257
Регистрация: 01.06.2013
Сообщений: 4,527
Записей в блоге: 9
04.08.2017, 16:50 4
Цитата Сообщение от tstusreg Посмотреть сообщение
первоначально не было известно какой размер будет у матриц
это неважно. По первой матрице определяете кол-во столбцов и строк. У следующих матриц столбцов должно быть столько же, а если строк оказалось меньше - уменьшаем суммарную матрицу. Нам же минимальная по размеру на выходе нужна.
Собственно, в задаче нет специфически матричных преобразований, можно одномерный вектор использовать, только запомнить кол-во столбцов что бы потом вывести как матрицу нужного размера.
0
04.08.2017, 16:50
tstusreg
4 / 4 / 0
Регистрация: 04.08.2017
Сообщений: 6
04.08.2017, 19:00  [ТС] 5
Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
это неважно.
Для идентичности бенчмарка это было важно, я понимаю что мы сравниваем кислое с длинным, возможно unboxed с boxed и т.д., но в грубом приближении можно сказать, что мы сравниваем две программы, выполняющие одну и ту же функцию, одним и тем же алгоритмом, но на разных языках.

Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
Собственно, в задаче нет специфически матричных преобразований, можно одномерный вектор использовать, только запомнить кол-во столбцов что бы потом вывести как матрицу нужного размера.
это понятно, но проблема не в вычислениях - они примитивные, проблема в вводе-выводе, профайлер показывает, что основные тормоза происходят в функциях, которые данные конвертируют туда-сюда (makeRow, toStrRow, readMFile), поможет в них замена на вектор?
0
Curry
2993 / 2074 / 257
Регистрация: 01.06.2013
Сообщений: 4,527
Записей в блоге: 9
04.08.2017, 20:24 6
Цитата Сообщение от tstusreg Посмотреть сообщение
основные тормоза происходят в функциях, которые данные конвертируют туда-сюда
Конвертация тормозит по той же причине - строка String в Haskell, это тоже список символов. Зря вы сразу после чтения из файла конвертируете Data.Text в String. words и lines есть и для Text.
Можно попробовать Data.Text.Lazy,
Data.Text.Lazy.Read
и особенно для вывода:
Data.Text.Lazy.Builder
https://www.stackage.org/haddock/lts...RealFloat.html


В вашем случае, конечно, быстрее были бы https://www.stackage.org/haddock/lts...ing-Char8.html,
https://www.stackage.org/haddock/lts...azy-Char8.html
но вы с питоном сравниваете, там строки наверняка utf8 или типа того.
1
tstusreg
4 / 4 / 0
Регистрация: 04.08.2017
Сообщений: 6
04.08.2017, 22:44  [ТС] 7
Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
words и lines есть и для Text.
Вот на это я как-то не обратил внимание, спасибо!

переписал makeRow следующим образом:

Haskell
1
2
makeRow :: T.Text -> TRow
makeRow = map fst . rights . map double . T.words
Было:
Haskell: 2.82sec
Python: 0.25sec

Стало:
Haskell: 0.89sec


Попробую сделать с Lazy.Builder, может еще ускорится...
2
korvin_
2750 / 2022 / 364
Регистрация: 28.04.2012
Сообщений: 6,901
04.08.2017, 23:07 8
Цитата Сообщение от tstusreg Посмотреть сообщение
Просто решение на питоне было прототипным и первоначально не было известно какой размер будет у матриц, однако по производительности оно почти полностью удовлетворяет, от прямого переписывания хотелось дешевого хаскельного чуда, а чудо не произошло, теперь надо будет копать дальше

Не по теме:

Если тебе нужна производительность, взял бы Си, например. Не, Хаскелл, конечно, в общем случае, должен быть быстрей Питона, но там не так очевидно (привычно) писать производительные программы, и вообще он больше про корректность.



Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
а если строк оказалось меньше - уменьшаем суммарную матрицу
А если больше? Я так понимаю, суммировать с нулями, но при этом нельзя уменьшать, т.к. потеряются предыдущие данные. Хотя это больше вопрос к tstusreg, я в код не сильно вчитывался.

Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
но вы с питоном сравниваете, там строки наверняка utf8 или типа того.

Не по теме:

Для ASCII это практически не важно.



KolodeznyDiver, может ещё немного поможет, если в некоторых функциях форсировать вычисление аргументов, чтобы избавиться от лишней ленивости?
0
Curry
2993 / 2074 / 257
Регистрация: 01.06.2013
Сообщений: 4,527
Записей в блоге: 9
04.08.2017, 23:17 9
Цитата Сообщение от korvin_ Посмотреть сообщение
KolodeznyDiver, может ещё немного поможет, если в некоторых функциях форсировать вычисление аргументов, чтобы избавиться от лишней ленивости?
В некоторых, может быть. После избавления от списков.
0
tstusreg
4 / 4 / 0
Регистрация: 04.08.2017
Сообщений: 6
05.08.2017, 13:45  [ТС] 10
Цитата Сообщение от korvin_ Посмотреть сообщение
Если тебе нужна производительность, взял бы Си, например. Не, Хаскелл, конечно, в общем случае, должен быть быстрей Питона, но там не так очевидно (привычно) писать производительные программы, и вообще он больше про корректность.
Питоновское решение меня полностью устраивает, если будет совсем проседать производительность - перепишу. Пример чисто учебный для меня, тем более не вижу никаких противоречий по поводу использования хаскелла, даже вон, на википедии пишут, что язык общего назначения, я же не на брейнфаке сел эту задачу писать...

Цитата Сообщение от korvin_ Посмотреть сообщение
А если больше? Я так понимаю, суммировать с нулями, но при этом нельзя уменьшать, т.к. потеряются предыдущие данные. Хотя это больше вопрос к tstusreg, я в код не сильно вчитывался.
Пусть теряются, их проще позже пересчитать, усреднение по самой короткой матрице...

Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
В некоторых, может быть. После избавления от списков.
Пока затык в функции toStrRow и конвертации матрицы из [[Double]] -> String, не нашел пока быстрого способа сделать [[Double]] -> Text

Добавлено через 13 часов 47 минут
Удалось сравняться с питоном в данной задаче по скорости, как бы странно это не звучало!

поставил пакет double-conversion

и поменял show на toShortest, который по заявлениям авторов работает в 30 раз быстрее:

Haskell
1
2
toStrRow :: TRow -> T.Text
toStrRow = T.unwords . map T.toShortest
В 30 раз ускорится не получилось, но раза в 3 ускорился
1
Curry
2993 / 2074 / 257
Регистрация: 01.06.2013
Сообщений: 4,527
Записей в блоге: 9
05.08.2017, 14:28 11
Цитата Сообщение от tstusreg Посмотреть сообщение
поставил пакет double-conversion
Вообще то toShortest выводит в другом формате чем show (и оба не в том, как во входных файлах).
А как питон выводит, так же? Для бенчей нужно подобрать одинаковый формат вывода, а то даже разные размеры файлов получаются.

Можно ещё немного ускорить использовав пакет text-show
Haskell
1
2
3
4
5
import qualified TextShow as TS
import TextShow (unwordsB,unlinesB)
 
showFMatrix :: FMatrix -> T.Text
showFMatrix = TS.toText . unlinesB . map (unwordsB . map (TS.fromText . toShortest))
1
tstusreg
4 / 4 / 0
Регистрация: 04.08.2017
Сообщений: 6
05.08.2017, 17:28  [ТС] 12
с TextShow на пяти 100 мб файлах, выигрыш получился примерно 3% от haskell-text и 6% от python

Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
А как питон выводит, так же? Для бенчей нужно подобрать одинаковый формат вывода
я попытался сделать round, но питоновский, выводит через пень колоду, то в scientific формате, то нормально округляя до определенного знака, в итоге добился более-менее похожего результата, округляя до 1 знака без дробной части, в итоге 22.3sec питон, 20.3sec хаскелл, такое же время было и раньше +/- погрешность.
1
dsorokin
57 / 42 / 1
Регистрация: 25.06.2015
Сообщений: 69
06.08.2017, 20:40 13
В Haskell есть мутабельные unboxed-массивы MArray IOUArray Double IO. При правильном использовании получается код на уровне Си или очень близко
0
Curry
2993 / 2074 / 257
Регистрация: 01.06.2013
Сообщений: 4,527
Записей в блоге: 9
06.08.2017, 21:21 14
Цитата Сообщение от dsorokin Посмотреть сообщение
В Haskell есть мутабельные unboxed-массивы MArray IOUArray Double IO. При правильном использовании получается код на уровне Си или очень близко
Тогда уж https://www.stackage.org/haddock/lts...d-Mutable.html

Добавлено через 18 минут
В данной задаче не нужен ассоциативный массив.
0
06.08.2017, 21:21
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.08.2017, 21:21

Не могу получить ответ от python скрипта и на его основе создать список (зависимые списки js ajax python)
Привет! Есть необходимость сделать динамические списки при помощи js, ajax jQuery, Python. Данные...

Почему синтаксис Python 2.* и Python 3.* так отличается?
Привет! Решил на досуге заняться изучением Python'a. Читаю книгу по второму питону, а пользуюсь...

Что лучше учить Python 2 или Python 3?
хочу начать учить питон но полазив в нете, частенько попадалась информация что вроде как 2 будет...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.