and fact I decided to learn rust (programming language) I've set up new instance of Eclipse and installed Eclipse Corrosion directly of marketspace...
All seems to be working well including Eclipse familiar debugging and variables inspectors

Code: Select all
pub fn dot_product(xs: &[f32], ys: &[f32]) -> f32 {
xs.iter()
.zip(ys)
.fold(Fast(0.), |acc, (&x, &y)| acc + Fast(x) * Fast(y))
.get()
}
pub fn convolution_serial(sample: &[f32], coeff: &[f32]) -> Vec<f32> {
sample
.windows(coeff.len())
.map(|window| dot_product(window, coeff))
.collect()
}
pub fn convolution_parallel(sample: &[f32], coeff: &[f32]) -> Vec<f32> {
sample
.par_windows(coeff.len())
.map(|window| dot_product(window, coeff))
.collect()
}
I've lived through rise and fall of Eclipse and how people slated it while alternatives weren't any better...
Actually, today is my second day really. I've managed (just) to port my Python code which was quite OO (StreamField extended to StreamByteField, StreamWordField, ... with StreamDefinition having list of StreamFields where StreamField defines some behaviour like to_json() and then re-implemented in specific fields) with 'not-that-many-repetitions'Heater wrote: ↑Fri Jun 26, 2020 2:03 pmHow are you getting on with Rust? I have been getting into Rust, slowly, for nearly a year. It's been fascinating if somewhat frustrating at times. It's amazing how one can get code to run as fast as C and C++ whilst being sure you have no weird memory usage errors creeping in.
Yes, I can see it! It pretty much re-assembles Java's streams (and, I am sure many other languages' similar concepts). I had some feeling that people who wrote some of Rust libraries did they homework well and made quite a few functional(-alike) stuff built in. So, I am quite excited to learn more. And, thanks for the example - you won't believe how much more I've learnt from it, too!
Wow, that is nice to hear.
That is a surprising statement to me.
I have read things like that many times. Problem is it is by no means always true. As I said, I have received many suggestions for "functional" style solutions to my Rust experiments. Mostly the suggestions were slower that what I had already and impossible to read as well. Do have a read of that rust-lang thread for some good (bad) examples.
Code: Select all
pub fn convolution_safe(sample: &[f32], coeff: &[f32]) -> Vec<f32> {
let mut out: Vec<f32> = vec![0.0; sample.len() - coeff.len() + 1];
for i in 0..out.len() {
let mut acc: f32 = 0.0;
let window = &sample[i..i + coeff.len()];
for j in 0..window.len() {
acc += window[j] * coeff[j];
}
out[ i ] = acc;
}
out
}
I'm sorry I might have mislead you with my statement. Approach to memory handling in Rust is same (similar) as with other languages - allocating structs on stack or heap - nothing new there. But, how references and values are tracked at compile time - that's completely new and original.Heater wrote: ↑Fri Jun 26, 2020 6:59 pmThat is a surprising statement to me.
I have never seen any other programming language with the approach to memory that Rust has. What with it's rules about multiple aliases to variables, the "borrow checker", no garbage collector and all that. As far as I know this is all very new.
Yeah, hence my hope I'll be able to confirm that it is better with some Rust libraries...
Yeah, it is so easy to lose track of what is really happening to code when people move one level up...Heater wrote: ↑Fri Jun 26, 2020 6:59 pmAs I said, I have received many suggestions for "functional" style solutions to my Rust experiments. Mostly the suggestions were slower that what I had already and impossible to read as well. Do have a read of that rust-lang thread for some good (bad) examples.
I'm yet to get that far!Heater wrote: ↑Fri Jun 26, 2020 6:59 pmWhat I found is that if you write Rust in a C style with typical arrays and "for" loops it will be slower than C. Because it has to add run-time array bounds checking and it cannot use vector instructions as a consequence.
You can reach C speeds by helping Rust with those array bounds. Use slices for example. Like this version of the convolution:pub fn convolution_safe(sample: &[f32], coeff: &[f32]) -> Vec<f32> {
let mut out: Vec<f32> = vec![0.0; sample.len() - coeff.len() + 1];
for i in 0..out.len() {
let mut acc: f32 = 0.0;
let window = &sample[i..i + coeff.len()];
for j in 0..window.len() {
acc += window[j] * coeff[j];
}
out = acc;
}
out
}
That runs as fast as C and as fast as the functional Rust style above.
It is the "restrict" keyword in C.
Except that in C restrict is basically just a hint to the compiler that anything accessed through a restricted pointer cannot be accessed via any other restricted pointer (within scope) and the compiler can generate code that assumes so. C will not enforce it, it will happily let you have two restricted pointers that access the same variable (or memory locations) at the same time and if you do that, it immediately becomes undefined behaviour.
The "restrict" keyword in C does not do anything for enforcing rules about pointers aliasing the same data or preventing multiple references to that same shared mutable data. It is only a hint to the compiler that it can assume pointers do not refer to the same data so that it might perform optimizations it might otherwise not do.
Code: Select all
void convolution(float *out, int *out_length, const float *sample, int samplelen, const float *coeff, int coefflen) {
int outlen = samplelen - coefflen + 1;
for (int i = 0; i < outlen; i++) {
float acc = 0.;
for (int j = 0; j < coefflen; j++) {
acc += sample[i + j] * coeff[j];
}
out[i] = acc;
}
*out_length = outlen;
}
What does Fast mean in the code? Would it be possible to use an FFT to perform that convolution? I wonder what a parallel bubble sort looks like.Heater wrote: ↑Fri Jun 26, 2020 2:03 pmI have never been into Eclipse much. It always seemed so big, slow and complicated when I have had to use it on various projects. But this looks pretty cool.
How are you getting on with Rust? I have been getting into Rust, slowly, for nearly a year. It's been fascinating if somewhat frustrating at times. It's amazing how one can get code to run as fast as C and C++ whilst being sure you have no weird memory usage errors creeping in.
One thing that sold me on Rust, again, a few days ago was the ease with which one could get code to distribute work over many cores and get a commensurate performance boost. For example:Can you spot the difference between the serial and parallel versions of convolution there?Code: Select all
pub fn dot_product(xs: &[f32], ys: &[f32]) -> f32 { xs.iter() .zip(ys) .fold(Fast(0.), |acc, (&x, &y)| acc + Fast(x) * Fast(y)) .get() } pub fn convolution_serial(sample: &[f32], coeff: &[f32]) -> Vec<f32> { sample .windows(coeff.len()) .map(|window| dot_product(window, coeff)) .collect() } pub fn convolution_parallel(sample: &[f32], coeff: &[f32]) -> Vec<f32> { sample .par_windows(coeff.len()) .map(|window| dot_product(window, coeff)) .collect() }
Magic!
A good question. It's not totally clear to me.
I have heard rumor that such things are possible. I have no idea what that is about or how.
Then it doesn't match the original C - because its a different algorithm. You could add threading to the C version and then compare them.Heater wrote: ↑Sat Jun 27, 2020 4:22 amThe challenge originally posted on the rust-lang forum involved a guy converting some C code to Rust and whining that it was so slow. It runs a 500 element convolution kernel over a 20 million element data set. That solution I posted above matches the speed of the original C as a single thread gets 3.9 times faster on 4 cores just by adding the "par_". Sweet.
On the contrary it is exactly the same algorithm. Maybe that is clear if I do the dot_product part in-line. This is the original C code:
Code: Select all
void convolution_c(float *out, int *out_length, const float *sample, int samplelen, const float *coeff, int coefflen) {
int outlen = samplelen - coefflen + 1;
for (int i=0; i<outlen; i++) {
float acc = 0.;
for (int j=0; j<coefflen; j++) {
acc += sample[i + j] * coeff[j];
}
out[i] = acc;
}
*out_length = outlen;
}
Code: Select all
pub fn convolution_serial(sample: &[f32], coeff: &[f32]) -> Vec<f32> {
sample
// Get sequence of windows that "slide" over the sample data
.windows(coeff.len())
// Form the dot product of every window in the sequence
.map(|window| {
window
.iter()
.zip(coeff)
.fold(Fast(0.), |acc, (&x, &y)| acc + Fast(x) * Fast(y))
.get()
})
// Map produces an iterator so we have to assemble a vector from that.
.collect()
}
OK, let's ignore threading for now. Serial code only as above.
That is exactly what we have here.
Good point. Luckily it's easy to call C functions from Rust and make the comparative timings. Both Rust and C end up going through the same LLVM backend and being subject to the same compiler flags. Which oddly enough I had already done. Here is an example result on my PC:
Code: Select all
$ cargo build --release
$ ./target/release/convolution
Original C: Duration 1334ms
119.04485 127.62263
Rust - serial: Duration 1289ms
119.04485 127.62263
No, not like that at all. Not like comparing a Fast Fourier Transform to a naive Fourier Transform either. The same algorithm is used in both cases.
A good idea. I will leave that as an exercise for the reader.
I saw in your original post
You can see why I thought threading had been added to the Rust version !! Algol68 had the "par" keyword to convert a normal serial clause (sequence of statements in curly brackets) into a parallel clause.original C as a single thread gets 3.9 times faster on 4 cores just by adding the "par_"
True.
Just a simple .....
Code: Select all
/tmp/convolution $ cargo run --release
Updating crates.io index
Downloaded instant v0.1.4
Downloaded fast-floats v0.1.2
Downloaded rayon v1.3.0
Downloaded cc v1.0.54
Downloaded crossbeam-deque v0.7.3
Downloaded either v1.5.3
Downloaded simdeez v1.0.6
Downloaded crossbeam-utils v0.7.2
Downloaded maybe-uninit v2.0.0
Downloaded crossbeam-epoch v0.8.2
Downloaded cfg-if v0.1.10
Downloaded rayon-core v1.7.0
Downloaded lazy_static v1.4.0
Downloaded paste v0.1.16
Downloaded memoffset v0.5.4
Downloaded autocfg v1.0.0
Downloaded scopeguard v1.1.0
Downloaded crossbeam-queue v0.2.3
Downloaded num_cpus v1.13.0
Downloaded paste-impl v0.1.16
Downloaded proc-macro-hack v0.5.16
Downloaded proc-macro2 v1.0.18
Downloaded quote v1.0.7
Downloaded syn v1.0.31
Downloaded libc v0.2.71
Downloaded unicode-xid v0.2.0
Compiling autocfg v1.0.0
Compiling proc-macro2 v1.0.18
Compiling maybe-uninit v2.0.0
Compiling cfg-if v0.1.10
Compiling unicode-xid v0.2.0
Compiling lazy_static v1.4.0
Compiling syn v1.0.31
Compiling libc v0.2.71
Compiling scopeguard v1.1.0
Compiling proc-macro-hack v0.5.16
Compiling rayon-core v1.7.0
Compiling cc v1.0.54
Compiling either v1.5.3
Compiling fast-floats v0.1.2
error[E0554]: `#![feature]` may not be used on the stable release channel
--> /home/jah/.cargo/registry/src/github.com-1ecc6299db9ec823/fast-floats-0.1.2/src/lib.rs:18:1
|
18 | #![feature(core_intrinsics)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0554`.
error: could not compile `fast-floats`.
warning: build failed, waiting for other jobs to finish...
error: build failed
Code: Select all
pi@raspberrypi:/tmp $ tar xf convolution.tgz
pi@raspberrypi:/tmp $ cd convolution/
pi@raspberrypi:/tmp/convolution $ cargo run --release
error: failed to parse lock file at: /tmp/convolution/Cargo.lock
Caused by:
invalid serialized PackageId for key `package.dependencies`
pi@raspberrypi:/tmp/convolution $
Code: Select all
pi@desktop-pi:~/temp/t/convolution (master) $ cargo run --release
Downloaded cfg-if v0.1.10
Downloaded lazy_static v1.4.0
Downloaded memoffset v0.5.4
Downloaded proc-macro2 v1.0.18
Downloaded unicode-xid v0.2.0
Downloaded num_cpus v1.13.0
Downloaded simdeez v1.0.6
Downloaded crossbeam-utils v0.7.2
Downloaded syn v1.0.31
Downloaded proc-macro-hack v0.5.16
Downloaded quote v1.0.7
Downloaded rayon-core v1.7.0
Downloaded crossbeam-queue v0.2.3
Downloaded cc v1.0.54
Downloaded paste v0.1.16
Downloaded crossbeam-deque v0.7.3
Downloaded instant v0.1.4
Downloaded scopeguard v1.1.0
Downloaded either v1.5.3
Downloaded crossbeam-epoch v0.8.2
Downloaded autocfg v1.0.0
Downloaded paste-impl v0.1.16
Downloaded maybe-uninit v2.0.0
Downloaded fast-floats v0.1.2
Downloaded rayon v1.3.0
Downloaded 25 crates (880.5 KB) in 1.65s
Compiling autocfg v1.0.0
Compiling cfg-if v0.1.10
Compiling proc-macro2 v1.0.18
Compiling maybe-uninit v2.0.0
Compiling unicode-xid v0.2.0
Compiling lazy_static v1.4.0
Compiling libc v0.2.71
Compiling syn v1.0.31
Compiling scopeguard v1.1.0
Compiling rayon-core v1.7.0
Compiling proc-macro-hack v0.5.16
Compiling cc v1.0.54
Compiling either v1.5.3
Compiling fast-floats v0.1.2
error[E0554]: `#![feature]` may not be used on the stable release channel
--> /home/pi/.cargo/registry/src/github.com-1ecc6299db9ec823/fast-floats-0.1.2/src/lib.rs:18:1
|
18 | #![feature(core_intrinsics)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0554`.
error: could not compile `fast-floats`.
To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed
Code: Select all
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ source $HOME/.cargo/env
$ rustup install nightly
$ cargo run --release
I just did:-
Adds to the fun!
Code: Select all
Compiling either v1.5.3
Compiling instant v0.1.4
Compiling fast-floats v0.1.2
error[E0554]: `#![feature]` may not be used on the stable release channel
--> /home/pi/.cargo/registry/src/github.com-1ecc6299db9ec823/fast-floats-0.1.2/src/lib.rs:18:1
|
18 | #![feature(core_intrinsics)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error