Найти - Пользователи
Полная версия: numpy оптимизация
Начало » Python для экспертов » numpy оптимизация
1 2 3
marina932
Вот код, который был у меня на python. Это финальная версия, которая у меня получилась на python. Она съедает около 450 метров памяти. 10 гб у меня выжирала, какая-то из промежуточных реализаций (я баловалась с процессами, потоками и т д). Я переписывала раза 4 код.

izekia я даже не думала кидать камни в сторону python. Это один из моих любимых языков, просто столкнулась с ситуацией, когда выгодней и проще использовать другой язык, где без использования сторонних библиотек и в лоб задача решается оптимально по скорости и потребляемым ресурсам. Все таки для каждой задачи свой инструмент.
 import math
import queue
import threading
import terminaltables
import numpy as np
from config import ConfigParser
def calc_entropy(alphabet: dict):
    return sum([-p * math.log(p, 2) for p in alphabet.values()])
def calc_parts(str_len: int, parts: int):
    """
    Считает какой длинны генерировать строку каждому из процессов
    :param str_len: общая длинна строки
    """
    parts = [str_len // parts] * parts
    diff = str_len - sum(parts)
    if diff != 0:
        parts[-1] += diff
    return parts
def generate_part_text(alphabet, length, res_str, res_frequency):
    """
    Генерирует строку с заданной вероястностью появления символов и считает
    сколько раз каждый из символов встречается в строке
    :param alphabet:
    :param length: длинна строки, которую надо сгенерировать
    :param res_str: очередь в которую помещается массив сгенеровынных символов
    :param res_frequency: очередь в котрую помещается словарь со списком
    символов и частотой его встречаемости
    """
    part = np.random.choice(
        list(alphabet.keys()), p=list(alphabet.values()), size=length
    )
    res_str.put_nowait(part)
    res_frequency.put_nowait(dict(zip(*np.unique(part, return_counts=True))))
def generate_text(alphabet: dict, length: int):
    result_str = queue.Queue()
    result_frequency = queue.Queue()
    count_thread = 6
    if length < 10000000:
        count_thread = 1
        generate_part_text(alphabet, length, result_str, result_frequency)
    else:
        all_tasks = []
        for i in calc_parts(length, count_thread):
            thread = threading.Thread(
                target=generate_part_text,
                args=(alphabet, i, result_str, result_frequency)
            )
            thread.start()
            all_tasks.append(thread)
        for i in all_tasks:
            i.join()
    # Объединятся результаты расчитанные в потоках
    text = np.array([], dtype='<U1')
    frequency = {}
    for i in range(count_thread):
        text = np.append(text, result_str.get_nowait())
        for key, value in result_frequency.get_nowait().items():
            frequency[key] = frequency.setdefault(key, 0) + value
    return text, frequency
conf = ConfigParser()
text, frequency = generate_text(conf.alphabet, conf.length)
with open('out.txt', 'w') as out:
    out.write(text.view('U{}'.format(conf.length))[0])
data_table = [['Символ', 'Количество выпадений']]
data_table.extend(frequency.items())
table = terminaltables.AsciiTable(data_table)
print(table.table)
print('Общая длинна строки: {}\nЭнтропия: {}'.format(
    conf.length, calc_entropy(conf.alphabet))
)

модуль config
 import os
import configparser
from collections import OrderedDict
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
class ConfigParser:
    def __init__(self):
        self._config = configparser.ConfigParser()
        self.length = 0
        self.alphabet = OrderedDict()
        self.read()
    def read(self):
        self.alphabet.clear()
        self._config.read(os.path.join(BASE_DIR, 'config.conf'))
        for section in self._config.sections():
            for i in self._config[section]['alphabet'].split(', '):
                i = i.split(':')
                self.alphabet[i[0]] = float(i[1])
            if sum([i for i in self.alphabet.values()]) != 1.0:
                print('Сумма вероятностей должна быть равна 1.0')
                exit()
            self.length = int(self._config[section]['length'])

[general]
alphabet = a:0.1, b:0.3, c:0.6
length = 100000000
izekia
marina932
Вот код, который был у меня на python. Это финальная версия, которая у меня получилась на python. Она съедает около 450 метров памяти. 10 гб у меня выжирала, какая-то из промежуточных реализаций (я баловалась с процессами, потоками и т д). Я переписывала раза 4 код.
ага, 450 похоже) 400 - это массив и там остальное питон.
По поводу для каждой задачи свое, мне этим питон и нравится: если меня что-то не устроит, я могу опуститься на, практически, самый низкий уровень, и на том же питоне с небольшими дополнениями, реализовать узкое место, и тут же, в том-же модуле написать код на совершенно другом уровне абстракции. Поэтому с этим утверждением не могу согласиться)

Жаль, что я на форум вернулся чуть позже, и тема стала неактуальна. А вообще, давайте, в свободное время накидаю окончательную реализацию, и Вы сравните у себя с го?
marina932
Лучше не тратьте время на это, потому что я проблему свою уже решила и проблема закрыта и не актуальна. Что-то писать сейчас это просто тратить время.

izekia
я могу опуститься на, практически, самый низкий уровень, и на том же питоне с небольшими дополнениями, реализовать узкое место
Речь идет про расширения на си или cython?

P.S ну и конечно хочется заметить, что сравнивать супер оптимизированный вариант на python с вариантом го, который писал человек работающий с ним несколько дней всего лишь это не корректно.
izekia
marina932
Речь идет про расширения на си или cython?
cython, конечно

marina932
Лучше не тратьте время на это, потому что я проблему свою уже решила и проблема закрыта и не актуальна. Что-то писать сейчас это просто тратить время.
я не трачу время, это пишется быстро, мне самому интересно до какой степени это все можно оптимизировать
marina932
izekia
cython, конечно
Это вообще ни как нельзя назвать “могу спуститься к более низкому уровню абстракций”, это написание расширения на компилируемом языке (с помощью довольно удобного инструмента конечно), но это уже не python. По сути писать расширения на си, можно к любым языкам и расширение на си сложно назвать кодом python.
izekia
marina932
это написание расширения на компилируемом языке (с помощью довольно удобного инструмента конечно), но это уже не python
cython - это надмножество языка python.

вот просто как пример:
 def sumpy(n):
    res = 0
    for a in range(1, n + 1):
        res += a
    return res
 %timeit sumpy(1000000)
10 loops, best of 3: 289 ms per loop

далее просто скомпилируем его же с помощью cythona:
 %%cython
def sumpy1(n):
    res = 0
    for a in range(1, n + 1):
        res += a
    return res
 %timeit sumpy1(1000000)
10 loops, best of 3: 165 ms per loop
уже получается -40%

а теперь все что сделаем - это типизируем все значения, в том числе и возвращаемое.
cpdef указывает на то что эта функция будет доступна в контексте обычного питона, иначе можно было бы использовать cdef.
 %%cython 
cpdef int sumcy(int n):
    cdef int a, res = 0
    for a in range(1, n + 1):
        res += a
    return res
 %timeit sumcy(1000000)
1000 loops, best of 3: 865 µs per loop

В последнем примере я не добавил никакой новой семантики, ничего нового, кроме явного объявления типов, но прирост производительности просто потрясающий, конечно пример выдуманный, но тем не менее он демонстрирует то что это не другой язык - это просто расширение питона, причем даже такие простые дополнения в коде, позволяют получить колоссальный прирост в производительности.
izekia
всегда можно посмотреть во что транслируется код (я изменил определение функции, чтобы не было лишнего кода связанного с питоном)
 +2: cdef int sumcy(int n):
static int __pyx_f_46_cython_magic_3d048ac8c5c96bfcdd601bb78e692920_sumcy(int __pyx_v_n) {
  int __pyx_v_a;
  int __pyx_v_res;
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("sumcy", 0);
/*  */
  /* function exit code */
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
+3:     cdef int a, res = 0
  __pyx_v_res = 0;
+4:     for a in range(1, n + 1):
  __pyx_t_1 = (__pyx_v_n + 1);
  for (__pyx_t_2 = 1; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) {
    __pyx_v_a = __pyx_t_2;
+5:         res += a
    __pyx_v_res = (__pyx_v_res + __pyx_v_a);
  }
+6:     return res
  __pyx_r = __pyx_v_res;
  goto __pyx_L0;

но это как и в питоне всегда можно посмотреть байткод в который транслируется код на питоне.
marina932
Я прекрасно знаю, что такое cython. Моем мнение не изменилось. В общем свое мнение я высказала, переубеждать не собираюсь, у меня есть дела кроме этого.
doza_and
marina932
Моем мнение не изменилось. В общем свое мнение я высказала,
Да вас и не пытаются убедить, потому что непонятно в чем заключается ваше мнение, вы его невнятно высказали. Вы утверждали что питон потребляет в 100 раз больше памяти. Вам указали на очевидные ошибки при измерении и для питона и для Go. Вы жаловались что питон медленно выполняет код. Да это так.

marina932
у меня есть дела кроме этого.
Не обижайтесь, я наверное более опытный программист и позволю себе высказать совет (вы сами утверждали что в GO вы только начинаете писать). Похоже что у вас много дел потому что вы пишете очень неэффективные программы и очень многословно, что на GO что на питоне.
Ваша задача решается в пять строчек и похоже является разовой. Зачем тут конфиг парсер эксперименты с тредами и прочая лабуда? Если разовая посчитали 10 сек и забыли. Вы на эти эксперименты наверное день потратили. В результате программа отжирает огромный объем памяти и забивает работой все ядра процессора.
Если не разовая, то надо обосновать почему выборка 10000000, зачем вообще эти данные в память пихать когда в этом нет никакой необходимости, у меня есть подозрение что энтропия аналитически считается в этом случае. Ваше время лучше потратить на вывод аналитического выражения а не на распараллеливание или переписывания на Go.

Ну и решения конечно надо принимать на основе проверенных фактов а не на основе результатов незнам чего.

Я вообще не считаю питон самостоятельным языком это скорее средство интеграции программ на C. поэтому в данном случае наверное сравнение GO vs python + C. уместно. Если вы собираетесь пользоваться питоном то владеть C/C++ просто обязательно. С этой точки зрения изучение GO это почти бесполезная потеря времени.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB