У меня есть программа, которая должна повернуть много больших одномерных numpy массивов плаваний в разграниченные строки. Я нахожу эту операцию довольно медленной относительно математических операций в моей программе и задаюсь вопросом, существует ли способ ускорить ее. Например, рассмотрите следующий цикл, который берет 100 000 случайных чисел в массиве numpy и присоединяется к каждому массиву в разграниченную запятой строку.
import numpy as np
x = np.random.randn(100000)
for i in range(100):
",".join(map(str, x))
Этот цикл занимает приблизительно 20 секунд для завершения (общее количество, не каждый цикл). Напротив, полагайте, что 100 циклов чего-то как поэлементное умножение (x*x) взяли бы, чем один 1/10 секунды для завершения. Очевидно строковая операция соединения создает большое узкое место производительности; в моем реальном приложении это будет доминировать над общим временем выполнения. Это заставляет меня задаться вопросом, есть ли более быстрый путь, чем"", .join (карта (ул., x))? Так как карта () - то, где почти все время обработки происходит, это сводится к вопросу того, преобразовывает ли там более быстрое к пути очень большое количество чисел к строкам.
Очень хорошая запись о производительности различных методов конкатенации строк в Python: http://www.skymind.com/~ocrow/python_string/
Я немного удивлен, что некоторые из последних подходов работают так же хорошо, как и они, но похоже, что вы определенно можете найти там что-то, что будет работать для вас лучше, чем то, что вы там делаете.
Самый быстрый метод, упомянутый на сайте
Метод 6: Составление списка
def method6 (): return '' .join ([`num` for num in xrange (loop_count)]) { {1}}
Этот способ самый короткий. Я испорчу сюрприз и скажу, что он к тому же самый быстрый. Он очень компактен и к тому же довольно понятен. Создайте список чисел, используя понимание списка , а затем соедините их все вместе. Что может быть проще? Этот на самом деле является сокращенной версией метода 4, и он потребляет примерно такой же объем памяти. Однако это быстрее, потому что нам не нужно вызывать функцию list.append () каждый раз, обходя цикл .
Использование imap из itertools вместо map в коде OP дает мне улучшение примерно на 2-3%, что немного, но что-то, что может сочетаться с другими идеями, чтобы дать больше улучшений.
Лично я думаю, что если вы хотите большего, вам придется использовать что-то вроде Cython.
numpy.savetxt даже медленнее, чем string.join. ndarray.tofile (), похоже, не работает с StringIO.
Но я нашел более быстрый метод (по крайней мере, применительно к примеру OP на python2.5 с более низкой версией numpy):
import numpy as np
x = np.random.randn(100000)
for i in range(100):
(",%f"*100000)[1:] % tuple(x)
Похоже, строковый формат быстрее, чем строковое соединение, если у вас есть хорошо определенный формат, такой как в данном конкретном случае. Но мне интересно, зачем OP такая длинная строка плавающих чисел в памяти.
Новые версии numpy не показывают улучшения скорости.
Думаю, вы могли бы поэкспериментировать с numpy.savetxt
, передавая объект cStringIO.StringIO
как поддельный файл ...
Или, может быть, используя str (x)
и замену пробелов запятыми (правка: это не сработает, потому что str
делает многоточие для больших массивов: -s).
Поскольку целью этого было пересылка массива по сети, возможно, есть лучшие альтернативы (более эффективные как с точки зрения ЦП, так и с точки зрения пропускной способности). Тот, который я указал в комментарии к другому ответу, как кодировать двоичное представление массива в виде текстового блока Base64. Основным неудобством для этого быть оптимальным является то, что клиент, читающий кусок данных, должен иметь возможность делать неприятные вещи, такие как переинтерпретировать массив байтов как массив с плавающей запятой, что обычно не допускается в языках с типобезопасностью; но это можно сделать быстро с помощью вызова библиотеки C (и большинство языков предоставляют средства для этого).
Если вы не можете связываться с битами, всегда есть возможность обработать числа по одному для преобразования декодированных байтов в числа с плавающей запятой.
Да, и следите за порядком байтов машин при отправке данных по сети: преобразовать в сетевой порядок -> base64encode -> отправить | получить -> base64decode -> преобразовать в порядок хоста