Comments (15)
This piece of code freezes the repl when audio-samples
array is big enough:
(->> (python/enumerate audio-samples)
(map (fn [[idx item]]
(->> audio-frame-rate
(/ idx)
(* twoPiF)
Math/sin
(* item)
int)))
doall
(arr/array array-type))
I tried both implementations and ended up with one that relies on run-simple-string
as it was the fastest one. It's approximately 25-30 times faster than my original implementation, so I am more than satisfied with performance.
I will definitely stop by help wanted topic for future research, thanks for a tip and your help :)
from libpython-clj.
If you could share a little bit more context then I could do some benchmarks :)
My guess thought is that this part is adding a lot of unneccessary expensive overhead:
(-> (libpy/PySequence_GetItem samples idx)
wrap-pyobject
->jvm)
Maybe try something like:
(require '[libpython-clj.require :refer [require-python import-python]])
(require '[libpython-clj.python :as py])
(import-python)
(->> (python/enumerate samples)
(map (fn [[idx sample]]
(let [time (/ idx frame-rate)
gain (* scale
(Math/sin (* twoPiF time)))]
(int (* gain sample))))))
map-indexed
is another option
(map-indexed (fn [idx sample]
(let [time (/ idx frame-rate)
gain (* scale
(Math/sin (* twoPiF time)))]
(int (* gain sample))))
samples)
The key is, don't do
(-> (libpy/PySequence_GetItem samples idx) wrap-pyobject ->jvm)
if you don't have to!
from libpython-clj.
Thank you for a quick reply :) Unfortunately, I already tried both implementations:
map-indexed
intoarray.array
is two times slower than my implementationenumerate
intomap
intoarray.array
freezes the repl when lazy-seq is evaluated...
The main problem I'm solving is to find the way to traverse the python array straight away without creating intermediate clojure vector, as I have to get an output in a form of python array...
So I tried this implementation in order to work straight away with input python array, but it was also slow and inefficient...
(with-gil
(dorun (map-indexed
(fn [idx item]
(let [time (/ idx audio-frame-rate)
gain (* scale
(Math/sin (* twoPiF time)))]
(libpy/PySequence_SetItem samples
idx
(int (* gain
item))
)))
samples))
samples)
Is there a better way to perform such processing?
P.S. By the way samples
is just a python array of type "i", which contains samples of audio data with sample width of 2 bytes:
samples = array.array("i", audio_data)
from libpython-clj.
Do you think you could post the code that causes the freeze? I'd like to look at that separately.
So -- in general what I will say is that overall, libpython-clj
is heavily optimized for exploratory development and (ideally) predictable behavior perhaps at the expense of clock cycles in certain circumstances (although we're working on it).
There are a few optimizations if you're willing to sacrifice some readability. First, you can save quite a bit of overhead if you're willing to use a Python function and keep the data "in Python". Secondly, while lists and vectors are typically copied, more complex objects are bridged instead of copied. So, you might want to consider populating an array type followed by a doto
. This should also save a good amount of overhead.
For the first example:
(let [{{:strs [calculate_gains]} :globals}
(py/run-simple-string "
import math
def calculate_gain(samples, audio_frame_rate, scale, twoPiF, idx):
time = idx / audio_frame_rate
gain = scale * math.sin(twoPiF * time)
samples[idx] = int(gain*time)
def calculate_gains(samples, audio_frame_rate, scale, twoPiF):
for idx, sample in enumerate(samples):
calculate_gain(samples, audio_frame_rate, scale, twoPiF, idx)
")]
(def calculate-gains calculate_gains))
(my parens may be unbalanced, apologies)
Second option (zero copy route) may look something like:
(doto (arr/array samples)
step1
step2
step3
...etc)
I realize that may be a bit unsatisfactory -- this is partly where the need for library authors comes into play for making clean APIs on top of domain specific applications. @cnuernber is excellent at heavy data processing, he might have some additional suggestions.
from libpython-clj.
I'd also encourage you to stop by our help wanted topic. Lots of smart folks in there. Since you're working with numpy I'm sure we can leverage that since it's blazingly fast!
from libpython-clj.
Glad to hear it! One other thing I'd like to add is if you don't like the run-simple-string
approach you can also move that code to a .py
file and load it in with require-python
! You just have to make sure it's on your PYTHONPATH
.
We're hoping in the future that library authors will provide some cool APIs for things like audio processing. If you're interested in working on that or know anyone who is let me know :)
from libpython-clj.
Is that python array something that converts to a numpy object? For large arrays using numpy and zerocopy pathways is going to be a lot faster plus then you can use a parallel-for type thing to mutably update all the elements in place.
from libpython-clj.
Closing this as it appears @dragoon000320 is satisfied :-).
Lots of options, run-simple-string, :reload with your own custom python module, and zerocopy pathways to get a tensor/nio buffer you can manipulate however you like.
from libpython-clj.
@jjtolton I am currently working on implementation of clojure wrappers for some solid Python audio processing libraries. So yes I am interested in working on that :)
from libpython-clj.
@cnuernber if it's okay, could you please provide more info regarding zerocopy pathways and parallel-for, as I am not familiar with these. Maybe you can redirect me somewhere, where I read about it... Thank you in advance :)
from libpython-clj.
@dragoon000320 - Not directly but I wouldn't mind writing up a short document on how to do your exact pathway assuming 'array' is (or is convertible to without copying the data) a numpy array.
from libpython-clj.
;; ======================================================================
user> (require '[libpython-clj.python :as py])
nil
user> (require '[libpython-clj.require :refer [require-python]])
nil
user> (require '[tech.v2.datatype :as dtype])
nil
user> (require '[tech.parallel.for :refer [parallel-for]])
nil
user> (require-python '[numpy :as np])
:ok
user> (def samples (np/linspace 0 4 :num 10000 :dtype :float32))
#'user/samples
user> samples
[0.0000000e+00 4.0004001e-04 8.0008002e-04 ... 3.9991999e+00 3.9995999e+00
4.0000000e+00]
user> (def nio-buf (dtype/as-nio-buffer samples))
#'user/nio-buf
user> nio-buf
#object[java.nio.DirectFloatBufferU 0x6efd3494 "java.nio.DirectFloatBufferU[pos=0 lim=10000 cap=10000]"]
user> (let [audio-frame-rate (float 1000)
twoPiF (float (* 2 Math/PI))
scale (float 1.5)]
(parallel-for
idx
(dtype/ecount nio-buf)
(let [time (/ idx audio-frame-rate)
gain (* scale (float (Math/sin (* twoPiF time))))]
(.put nio-buf idx gain))))
nil
user>
user> samples
[ 0. 0.00942472 0.01884906 ... -0.02827004 -0.01884644
-0.00942209]
from libpython-clj.
There is a lot there that is key to getting things fast. Using explicit typing: (float x)
is key, don't use ^float x x
.
Also putting unchecked-math at the top is going to have an effect in this case.
If you want to really go fast then use the primitives from fastmath for the math. Same outline, just require/use fastmath for Math/sin and friends.
from libpython-clj.
@cnuernber Thank you for your help and effort, I will definitely try it in my code and also take a look at the tech API :)
from libpython-clj.
@dragoon000320 - you are welcome!
Also, there are some serious experts lurking on the zulip libpython-clj-dev area so I may have missed something or who knows but opening up the problem to more eyes will get some insightful comments I am sure.
from libpython-clj.
Related Issues (20)
- Cannot run the example in the tutorial in Cider with JDK 17 HOT 8
- process hanging in embeded mode on exception HOT 2
- from-import does not support "string"
- newbie: I get: `dir already refers to: #'clojure.repl/dir in namespace: user` HOT 2
- Newbie: Lein Clojure Windows Anaconda VSCode Cava REPL Returns (NoClassDefFoundError) HOT 5
- Create Python Module then Import Module and Call Function Inside Clojure? HOT 4
- Fix ubuntu version in dockerfiles HOT 6
- Consider releasing returned values from instance fns
- JDK-20 support HOT 3
- builtins/eval throws 'frame does not exist' HOT 2
- Unable to require libraries from within docker using conda HOT 6
- Failed to find a valid python library on MS-Windows with the official python distributions
- Extend CI coverage to all the mainstream architectures
- require-python missing functions with annotation HOT 3
- Make polyglot development easier by allow require-python to be path-informed HOT 1
- Data transformation pipelines? HOT 1
- NSWindow main thread error when trying to setup Gymnasium (openapi gym) HOT 5
- JVM crashes when requiring `libpython-clj2.require` on 64bit Raspberry Pi 4 HOT 1
- error of unhashable type HOT 15
- Ways to reduce probability of memory leaks? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from libpython-clj.