Уведомления

Группа в Telegram: @pythonsu

#1 Ноя. 8, 2016 04:44:07

izekia
От:
Зарегистрирован: 2008-07-19
Сообщения: 317
Репутация: +  12  -
Профиль   Отправить e-mail  

numpy оптимизация

на самом деле от внезапного сегфолта можно перестраховаться примерно так:

 %%cython -a 
#cython: boundscheck=False
import numpy as np
cimport numpy as np
cpdef trivial1_cyth1_safe(text):
    cdef unsigned char[:] text_int_view = np.array(text.view(np.uint8)[::4])
    cdef int[:] c = np.zeros(256, dtype=np.int)
    cdef int l = len(text_int_view)
    cdef int i
    for i in range(l):
        c[text_int_view[i]] += 1
    return {chr(i): c[i] for i in range(len(c)) if c[i] > 0}
правда получится чуть медленнее:
 %%timeit -n 5
trivial1_cyth1_safe(text)
5 loops, best of 3: 863 ms per loop
 trivial1_cyth1_safe(text) == trivial1_cyth1(text)
True

Но тогда уже быстрее получается с проверкой:
 %%cython -a 
import numpy as np
cimport numpy as np
cpdef trivial1_cyth1_boundcheck(text):
    cdef int[:] text_int_view = text.view(np.int)
    cdef int[:] c = np.zeros(256, dtype=np.int)
    cdef int l = len(text_int_view )
    cdef int i
    for i in range(l):
        c[text_int_view [i]] += 1
    return {chr(i): c[i] for i in range(len(c)) if c[i] > 0}
 %%timeit -n 5
trivial1_cyth1_boundcheck(text)
5 loops, best of 3: 725 ms per loop



Отредактировано izekia (Ноя. 8, 2016 04:48:25)

Офлайн

#2 Ноя. 8, 2016 06:38:04

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  252  -
Профиль   Отправить e-mail  

numpy оптимизация

izekia
странно, а я думал, что все методы numpy дико оптимизированы:
Так и есть. Просто у np.unique другой алгоритм. Мы использовали знания о том что различных элементов немного, их содержимое можно использовать как адрес, нам заранее известно их количество. Если этого не предполагать то и в с и ситоне тоже медленный алгоритм получится.



Офлайн

#3 Ноя. 8, 2016 06:58:29

izekia
От:
Зарегистрирован: 2008-07-19
Сообщения: 317
Репутация: +  12  -
Профиль   Отправить e-mail  

numpy оптимизация

doza_and
но тогда такой алгоритм должен был показать хорошие результаты:

 np.add.at(c, text.view(np.int), 1)
а этого не произошло

В случае юник, он конечно хеширует, это явно видно, когда заменяешь строки целыми. Но в этом варианте алгоритм аналогичен тому что используется с сифоном, или я что-то не понимаю?



Отредактировано izekia (Ноя. 8, 2016 06:59:26)

Офлайн

#4 Ноя. 8, 2016 16:42:48

marina932
Зарегистрирован: 2016-02-22
Сообщения: 25
Репутация: +  0  -
Профиль   Отправить e-mail  

numpy оптимизация

izekia
Спасибо за помощь, но уже вопрос не актуален.
Cython пробовала использовать, но возникли проблемы с компиляцией, слишком это проблемно без должно опыта (по крайней мере мне это так показалось).

В целом для себя проблему решила тем, что переписала все на go. Даже без распараллеливания все работает очень быстро и потребляет в сотни раз меньше памяти.

Отредактировано marina932 (Ноя. 8, 2016 16:43:04)

Офлайн

#5 Ноя. 8, 2016 19:25:16

izekia
От:
Зарегистрирован: 2008-07-19
Сообщения: 317
Репутация: +  12  -
Профиль   Отправить e-mail  

numpy оптимизация

marina932
да я по датам понял что неактуально, просто даже в какой-то момент самому интересно стало.
По памяти то что я писал на cython вообще не требует ее практически.
Только один килобайт на счетчики и там немного по мелочи. Думаю по скорости будет быстрее чем на go.

А по поводу cython, там не настолько все сложно, если представлять примерно что такое С, и каким образом все транслируется.



Офлайн

#6 Ноя. 8, 2016 21:08:40

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  252  -
Профиль   Отправить e-mail  

numpy оптимизация

marina932
все работает очень быстро и потребляет в сотни раз меньше памяти.
Так вы бы код выложили и все посмотрят и поймут что все сделано. Тут интересный вопрос, а как вы потребление памяти проверяете? Тут 100 Мегов минимум 100*100=10 Гигов? неужели у вас питон столько потребляет?
Да и сравнить быстродействие C и Go интересно.



Офлайн

#7 Ноя. 9, 2016 14:00:31

marina932
Зарегистрирован: 2016-02-22
Сообщения: 25
Репутация: +  0  -
Профиль   Отправить e-mail  

numpy оптимизация

doza_and
Тут 100 Мегов минимум 100*100=10 Гигов? неужели у вас питон столько потребляет?
Именно столько и выжирает.
Смотрела банально через диспетчер задач, потребление памяти для меня не страшный момент, по этому сильно не морочилась, над замерами.
Пиковое значение потребления памяти программы на go 71 мб

Код, файла где точка входа
package main

import (
"fmt"
"infotheory/generator/utils"
"io/ioutil"
"math"
"math/rand"
"time"
)

func getRandLetter(alphabet []rune, probability []float64) rune {
num := rand.Float64()
for index, currentProb := range probability {
if currentProb > num {
return alphabet[index]
}
}
return alphabet[len(probability) - 1]
}

func GenerateString(conf *utils.Config) ([]byte, map[byte]int, time.Duration) {
start := time.Now()

resultStr := make([]byte, conf.Length)
counter := make(map[byte]int)
var letter byte

for i := int64(0); i < conf.Length; i++ {
letter = byte(getRandLetter(conf.Alphabet, conf.Probability))
resultStr[i] = letter
counter[letter]++
}

return resultStr, counter, time.Since(start)
}

func calcEntropy(conf *utils.Config) float64 {
var res float64
for _, i := range conf.Probability {
res += -i * math.Log2(i)
}
return res
}

func init() {
rand.Seed(time.Now().UnixNano())
}

func main() {
conf := utils.ConfigParser()

text, counter, spentTime := GenerateString(&conf)
fmt.Println("Сгенерированна строка длинной:", conf.Length)
fmt.Println("Потрачено времени на генерацию строки и подсчет символов:", spentTime)
fmt.Println("Энтропия равна:", calcEntropy(&conf))
fmt.Println("Количество символов:")
for key, value := range counter {
fmt.Println(string(key), value)
}

err := ioutil.WriteFile("./out.txt", text, 0755)
if err != nil {
panic(err)
}
}

Парсер конфига
package utils

import (
"fmt"
"github.com/olebedev/config"
"strconv"
"os"
"strings"
)

type Config struct {
Length int64
Alphabet []rune
Probability []float64
}

func parseProbability(conf *config.Config, result *Config) {
Probability, err := conf.String("probability")
if err != nil {
panic(err)
}

prevProb := 0.0
for _, value := range strings.Split(Probability, ",") {
i, err := strconv.ParseFloat(value, 64)
if err != nil{
panic(err)
}
prevProb += i
result.Probability = append(result.Probability, prevProb)
}

if result.Probability[len(result.Probability)-1] != 1.0 {
panic(fmt.Sprintln("Сумма вероятностей не равна 1, проверьте конфигурацию", result.Probability))
}
}

func ConfigParser() Config {
conf, err := config.ParseJsonFile("/Users/alex/Golang/src/infotheory/generator/config.json")
if err != nil {
fmt.Println("Конфигурационный файл не найден")
os.Exit(1)
}

result := new(Config)

length, err := conf.String("length")
if err != nil {
panic(err)
}

result.Length, err = strconv.ParseInt(length, 0, 64)
if err != nil {
panic(err)
}

alphabet, err := conf.String("alphabet")
if err != nil {
panic(err)
}

for _, value := range strings.Split(alphabet, ",") {
result.Alphabet = append(result.Alphabet, rune([]byte(value)[0]))
}

parseProbability(conf, result)

return *result
}

Отредактировано marina932 (Ноя. 9, 2016 14:03:22)

Офлайн

#8 Ноя. 9, 2016 14:30:39

izekia
От:
Зарегистрирован: 2008-07-19
Сообщения: 317
Репутация: +  12  -
Профиль   Отправить e-mail  

numpy оптимизация

marina932
во-первых, конечно забавно сравнивать потребление памяти, когда в го Вы явно формируете алфавит из байтов, а в питоне из строк в юникоде

во-вторых, технически Вы не тратите время на повторный обход массива для подсчета символов

ну и в третьих, простите, но попробуйте объяснить, как массив из 100 000 000 элементов размером в байт умещается в “пиковые 71Мб”



Офлайн

#9 Ноя. 9, 2016 15:00:42

izekia
От:
Зарегистрирован: 2008-07-19
Сообщения: 317
Репутация: +  12  -
Профиль   Отправить e-mail  

numpy оптимизация

для начала это первоначальный вариант:

 import numpy as np
from memory_profiler import profile
from sys import getsizeof
  
def create_text(length):
    alphabet = {str(i - 1): np.float64(i / 55.0) for i in range(1, 11)}
    return np.random.choice(list(alphabet.keys()), p=list(alphabet.values()), size=length)
  
def f_prev(text):
    return {code: count for code, count in zip(*np.unique(text, return_counts=True))}
  
@profile
def main():
    text = create_text(100000000)
    print('Size of text:', getsizeof(text))
    result = f_prev(text)
    print(result)
  
if __name__ == '__main__':
    main()

 > python -m memory_profiler alphabet.py
Size of text: 400000096
{'3': 7279434, '9': 18185304, '4': 9093148, '7': 14545076, '0': 1817613, '1': 3635281, '2': 5452784, '6': 12721588, '8': 16367275, '5': 10902497}
Filename: alphabet.py
Line #    Mem usage    Increment   Line Contents
================================================
    15     46.6 MiB      0.0 MiB   @profile
    16                             def main():
    17    426.5 MiB    379.9 MiB       text = create_text(100000000)
    18    426.8 MiB      0.3 MiB       print('Size of text:', getsizeof(text))
    19    427.3 MiB      0.5 MiB       result = f_prev(text)
    20    427.3 MiB      0.0 MiB       print(result)



Офлайн

#10 Ноя. 9, 2016 18:11:44

izekia
От:
Зарегистрирован: 2008-07-19
Сообщения: 317
Репутация: +  12  -
Профиль   Отправить e-mail  

numpy оптимизация

так как в целом не так важно, отсортированы у нас символы или нет, так как данный алгоритм этим не пользуется, поэтому я немного упростил создание списка:

 from fractions import Fraction
from itertools import repeat, chain
from array import array
from memory_profiler import profile
from sys import getsizeof
  
ALPHABET_SIZE = 10
  
def create_text(length):
    alphabet = ''.join(str(i) for i in range(ALPHABET_SIZE)).encode('ascii')
    denominator = int(ALPHABET_SIZE * (ALPHABET_SIZE + 1) / 2)
    probabilities = [Fraction(numerator, denominator) for numerator in range(1, ALPHABET_SIZE + 1)]
  
    return bytes(chain.from_iterable(repeat(sym, round(prob * length)) for sym, prob in zip(alphabet, probabilities)))
  
def count_symbols(text):
    c = array('L', repeat(0, 256))
    for symbol in text:
        c[symbol] += 1
    return {chr(i): c[i] for i in range(len(c)) if c[i] > 0}
  
@profile
def main():
    text = create_text(100000000)
    print('Size of text:', getsizeof(text))
    result = count_symbols(text)
    print(result)
  
if __name__ == '__main__':
    main()

 > python -m memory_profiler alphabet_with_array.py
Size of text: 100000033
{'9': 18181818, '1': 3636364, '4': 9090909, '8': 16363636, '0': 1818182, '3': 7272727, '6': 12727273, '5': 10909091, '2': 5454545, '7': 14545455}
Filename: alphabet_with_array.py
  
Line #    Mem usage    Increment   Line Contents
================================================
    26     37.6 MiB      0.0 MiB   @profile
    27                             def main():
    28    133.3 MiB     95.6 MiB       text = create_text(100000000)
    29    133.6 MiB      0.3 MiB       print('Size of text:', getsizeof(text))
    30    133.7 MiB      0.0 MiB       result = count_symbols(text)
    31    133.7 MiB      0.0 MiB       print(result)

Это конечно не “71 Мб”, но и не в сто раз больше, замечу, что этот вариант не использует никакой оптимизации, здесь важно было показать, что стандартный питон не требователен к ресурсам и не “выжирает” память. Оптимизированный вариант напишу чуть позже.



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version