for(int i=0;i<10000; ++i){
// do nothing just compute hash again and again.
hash = str.hashCode();
}
https://github.com/MayankPratap/Samchika/blob/ebf45acad1963d..."do nothing" is correct, "again and again" not so much. Java caches the hash code for Strings and since the JIT knows that (at least in recent version[1]) it might even remove this loop entirely.
I'm actually pretty curious to see what this method does on versions that don't have the optimization to treat hashCodes as quasi-final.
A quick test using Java 17 shows it's not being optimized away _completely_, but it's taking...~1 ns per iteration, which is not enough to compute a hash code.
Edit: I'm being silly. It will just compute the hashcode the first time, and then repeatedly check that it's cached and return it. So the JIT doesn't have to do any _real_ work to make this skip the hash code calculation.
So most likely, the effective code is:
computeHashCode();
for (int i = 0; i < 10000; i++) {
if (false) { // pretend this wouldn't have dead code elimination, and the boolean is actually checked
computeHashCode();
}
}
Btw I found most of the jmh samples interesting. IMO a quite effective mix of example and documentation. (and I'm not sure there is even much other official documentation)
[1] https://github.com/openjdk/jmh/blob/master/jmh-samples/src/m... [2] https://github.com/openjdk/jmh/blob/872b7203c294d90c17766d19...
I will try to incorporate most of your feedback. Your commments have given me much to learn.
This project was started to just learn more about multithreading in a practical way. I think I succeeded with that.
The nasal "m" takes on the form of the nasal in the row/class of the letter that follows it. As "ñ" is the nasal of the "c" class, the "m" becomes "ñ"
Writing Sanskrit terms using the roman script without using something like IAST/ISO-15919 is a pain in the neck. They are going to be mispronounced one way or the other. I try to get the ISO-15919 form and strip away everything that is not a-z.
So, सञ्चिका (sañcikā) = sancika
You probably want to keep the "ch," as the average English speaker is not going to remember that the "c" is the "ch" of "cheese" and not "see."
All nasals becoming anusvaras is something Hindi/Marathi and other languages using the Devanagari script do. Sanskrit uses the specific form of the nasal when available.
Also, modern filing systems are all thread safe. You can have multiple threads reading and even writing in parallel on different CPU cores.
No, there is no separate kernel "executing". When you do a syscall, your thread becomes kernel mode and it executes the function behind the syscall, then when it's done, your thread reverts to user mode.
A context switch is when one thread is being swapped out for another. Now the syscall could internally spawn a thread and context switch to that, but I'm not sure if this happens in read() or any syscall for that matter.
Have the OS handle memory paging and buffering for you and then use Java's parallel algorithms to do concurrent processing.
Create a "MappedByteBuffer" and mmap the file into memory.
If the file is too large, use an "AsynchronousFileChannel" and asynchronously read + process segments of the buffer.
https://gavinray97.github.io/blog/panama-not-so-foreign-memo...
Then again, if you're in Java/JVM land you're probably not building bleeding edge DBs ala ScyllaDB. But I'm somewhat surprised at the lack of projects in this space. One would think this would pair well with some of the reactive stream implementations so that you wouldn't have to reimplement things like backpressure, etc.
b) SycllaDB is not bleeding edge. It uses the relatively old now DPDK.
c) There are countless reactive stream implementations e.g. https://vertx.io/docs/vertx-reactive-streams/java/
I'm very aware of various reactive stream impls - I was saying that this work should plug into them rather than reinventing the wheel.
It is not wrong, but at least put yourself into it a bit.
And this project is just a start.
You didn't learn anything. You didn't accomplish anything. And no one including you respects it.
ArrayList => use when reads total more than adds.
But even then, I'm pretty sure Go actually uses an array for it's green stacks nowadays, even while paying the copy penalty for expansion.
I've also written a a video processor around that time too that was bottle necked using ArrayLists - typically a decode, store and read once op. It was at this point I looked at other collections, other list implementations and blocking deques (ArrayList was the wrong collection type to use, but I'd been in a rush for MVP) and ultimately came across https://github.com/conversant/disruptor and used that instead.
The ArrayList Vs Linkedlist was a real eye opener for me in two different systems this same behaviour was replicated when using ArrayLists like queues or incorrect sizing of the buffer increments as load increases.
Anyway, I felt I had to run the benchmarks myself.
@Benchmark
@Fork(1)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public Object arrayListPreallocAddMillionNulls() {
ArrayList<Object> arrList = new ArrayList<>(1048576);
for (int i = 0; i <= 1_000_000; i++) {
arrList.add(null);
}
return arrList;
}
@Benchmark
@Fork(1)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public Object arrayListAddMillionNulls() {
ArrayList<Object> arrList = new ArrayList<>();
for (int i = 0; i <= 1_000_000; i++) {
arrList.add(null);
}
return arrList;
}
@Benchmark
@Fork(1)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public Object linkedListAddMillionNulls() {
LinkedList<Object> linkList = new LinkedList<>();
for (int i = 0; i <= 1_000_000; i++) {
linkList.add(null);
}
return linkList;
}
And as I expected, on JDK 8 ArrayList with an appropriate initial capacity was faster than LinkedList. Admittedly not an order of magnitude difference, only 1.7x. JDK8
Benchmark Mode Cnt Score Error Units
MyBenchmark.arrayListAddMillionNulls thrpt 5 229.950 ± 9.994 ops/s
MyBenchmark.arrayListPreallocAddMillionNulls thrpt 5 344.116 ± 7.070 ops/s
MyBenchmark.linkedListAddMillionNulls thrpt 5 199.446 ± 15.910 ops/s
But! On JDK 17 the situation is completely upside-down: JDK17
Benchmark Mode Cnt Score Error Units
MyBenchmark.arrayListAddMillionNulls thrpt 5 90.462 ± 18.576 ops/s
MyBenchmark.arrayListPreallocAddMillionNulls thrpt 5 214.079 ± 15.505 ops/s
MyBenchmark.linkedListAddMillionNulls thrpt 5 216.796 ± 19.392 ops/s
I wonder why ArrayList with default initial capacity got so much worse. Worth investigating further.This helps prove my point that adds (and deletes) are generally faster by default when not pre sizing, or removing.
Typically (in my experience) ArrayLists are used without thought to sizing, often because initial capacity and amount to resize, cannot be determined sensibly or consistently.
If in your example you were also to resize the lists, (perhaps adding then dropping those in the Fibonacci sequence?), it would help prove my statement further.
Certainly not worth the -2 points I got from making the statement, but hey you can "please some people some of the time..." :D