It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
im running a simple loop that prints out the iterator (i) for 1.000.000 times in both java and c.
im using netbeans and visual studio respectively.
i dont care about precision but at about 40 seconds:
netbeans (java) has printed about 500.000 numbers
while windows (c) has printed about 75.000 numbers
-- why such a big difference?
im using a common intel core2duo(2.0 Ghz) pc with windows7
That seems wrong. Could you provide your code?
My Versions:
C version compiled with gcc -std=c99 -o itr itr.c with gcc 4.5.1
#include <stdio.h>
int main( int argc, char **argv )
{
for ( int i = 0; i < 1000000; i++ )
{
printf("%d\n", i);
}
}
Java Version compiled as javac Itr.java with javac 1.6.0_20 and JVM being:
OpenJDK Runtime Environment (IcedTea6 1.9.1) (ArchLinux-6.b20_1.9.1-1-x86_64)
OpenJDK 64-Bit Server VM (build 17.0-b16, mixed mode)
code -
class Itr
{
public static void main( String[] av )
{
for ( int i = 0; i < 1000000; i++ )
{
System.out.println(i);
}
}
}
and the times:
time ./itr
// Snip Output //
real 0m1.964s
user 0m0.330s
sys 0m1.477s
time java Itr
// Snip Output //
real 0m5.245s
user 0m2.337s
sys 0m3.023s
The test system is a Intel Core i5 M520 ( # 2.4GHz ) running 64 bit ArchLinux.
One way to considerably speed up your example would be:
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000000; i++)
sb.append(i).append("\n");
System.out.println(sb.toString());
}
String concatenation or output (in your case printing to standard output stream) in a loop is bad by design and not the fault of Java, you just generally want to avoid that.
It is much faster if you minimize the calls to output and use a local buffer. Also concatenating Strings is also inefficient - Java has StringBuilder class for that task.
Without providing your code and environnement settings, your test have no value.
Are you sure that the NetBeans console display isn't slown down in C case, or optimized for Java output?
Are you sure you did run the two projects in optimized mode without debug? C debug versions often generate a lot of debug informations that clearly slow down everything if you're debugging. Anyway, any benchmark should be done with optimization AND no debug mode.
Related
This question already has answers here:
Why is std::cout so time consuming?
(1 answer)
'printf' vs. 'cout' in C++
(16 answers)
Closed 2 months ago.
I wrote a program in both C++ and Java to print "Hello World" 100,000 times, but I noticed that the C++ code takes too long compared to the Java code;
The Java code takes about 6 seconds averagely and the C++ code takes about 18 seconds averagely, both run from the command line;
Can someone please explain why, thanks.
The name of the program is first.java and first.cpp for Java and C++ respectively
I used: java first.java; and first.exe; both from the command line
g++ --version
g++ (Rev6, Built by MSYS2 project) 11.2.0
java --version
java 13.0.2, 2020-01-14
Java Code
class first {
public static void main(String... args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
System.out.println("Hello World");
}
long end = System.currentTimeMillis();
long dur = end - start;
System.out.println(dur / 1000);
}
}
C++ Code
#include <iostream>
#include <string>
#include <chrono>
using namespace std;
int main()
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < 100000; i++)
{
cout << "Hello World" << endl;
}
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
cout << elapsed_seconds.count() << endl;
}
There are several relevant differences between your C++ and Java code:
By default C++ IO streams synchronise their state with the underlying C streams. This takes time. To avoid this (which you can do only if you know that your code does not mix C and C++ IO operations!), add the following to the beginning of your main code:
std::ios_base::sync_with_stdio(false);
cout << endl; is equivalent to cout << "\n" << flush; (which, in turn, is equivalent to cout << "\n"; cout.flush();). The flush call is absent from your Java code. You could add it to your Java code or, better, remove it from your C++ code: you almost never need to use endl/flush. Instead, just use
cout << "Hello World\n";
As noted by Peter in the comments, most systems flush the stdout stream on newline anyway (at least when attached to a terminal) so one might expect this not to make a difference. However, it does make a (substantial!) difference e.g. when piping the output to a file.
Your Java benchmark code truncates fractional seconds. To show those fractions of seconds (relevant since the code runs in <1s!), change the relevant line to
System.out.println(dur / 1000.0);
Be sure to compile your C++ code with optimisations enabled; with GCC/clang/ICC, you do this by passing -O2. MSVC has a similar flag, /O2 (there are higher optimisation levels but they have particular issues; -O2 is pretty much the default setting people use).
Conversely, java first.java will first compile the code every time you invoke it. To make the comparison fair, be sure to run javac first.java ahead of time, and then execute the code via java first.
Making these changes causes the C++ code to overtake the Java code on my system. This is most noticeable when increasing the loop size from 100,000 to 1,000,000: the C++ code now runs in milliseconds, while the Java code takes several seconds (be sure to pipe the output to a file! Otherwise you will be purely measuring the latency/rendering speed of your terminal, not the performance of the code).
I create simple test which compare my Go and Java application performance
I do not know why but it looks like my Java application is faster than Go
I used:
~> go version
go version go1.15.6 darwin/amd64
and
~> java -version
openjdk version "15.0.1" 2020-10-20
OpenJDK Runtime Environment (build 15.0.1+9)
OpenJDK 64-Bit Server VM (build 15.0.1+9, mixed mode, sharing)
Go function mostly tested is:
func split(text string, occurrence map[string]int, separators []string) {
words := strings.Split(text, separators[0])
for _, w := range words {
if len(w) > 0 {
if len(separators) > 1 {
split(w, occurrence, separators[1:])
} else {
occurrence[w] = occurrence[w] + 1
}
}
}
}
Java equivalent:
private void split(String text, Map<String, Integer> occurrence, String[] separators) {
StringTokenizer st = new StringTokenizer(text, separators[0]);
while (st.hasMoreTokens()) {
if (separators.length > 1) {
split(st.nextToken(), occurrence, Arrays.copyOfRange(separators, 1, separators.length));
} else {
occurrence.compute(st.nextToken(),(k,v) -> v == null ? 1 : v+1);
}
}
}
Start 10 threads and execute this method against text loaded from ulyss10.txt file in the loop (text is loaded into memory once on the beginning of application execution - it is not I/O test).
There you can see all files from test: https://github.com/TOlchawa/go-vs-java/tree/main/book_read_test
My expectation was Go will be faster - but results are opposite.
It looks like Go is little bit slower - about: 40% slower (which is unexpected)
I know this is not a very reliable test - but nevertheless I'm surprised.
Could you provide me list of possible reasons why it was happen, please?
in my understanding it is difference between:
strings.Split | StringTokenizer
slider | HashMap
routine | Thread
go compiler | JVM
memory management | GC
differences in source code of application (IMHO it is not an issue)
what else ?
//edit
There was a wrong version in Github repo - but during my tests I used correct and the question is still valid/open.
Rather than doing a game of guessing. Here is what I would do to understand what happened.
Run a program profiling for both java and golang. Visualise them into memory allocation flow and flame graph.
See where each program spend most of their time.
Both golang and java has the same runtime speed for more/less equivalent low-level task that they do.
However, they underlying implementation for strings library can be different and hence lead to very huge performance difference.
For example, have you give a thought on how Golang vs Java implement a map? How about string splitting.
Any excessive bytes copy operations?
In real-world, performance optimisation are mostly cpu and memory management, not the "flashy" better big O algorithms. See Kafka vs RMQ. A lot of the performance edge came from better socket buffer management and zero-copy technique, which isn't rocket science algo at all.
Here I have Java and C code that tries to do an Atomic increment operation using CAS.
To increment an long variable from 0 to 500,000,000.
C : Time taken : 7300ms
Java : Time Taken : 2083ms
Can any one double check these results? Because I just can't believe them.
Thanks
Java code:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class SmallerCASTest {
public static void main(String[] args){
final long MAX = 500l * 1000l * 1000l;
final AtomicLong counter = new AtomicLong(0);
long start = System.nanoTime();
while (true) {
if (counter.incrementAndGet() >= MAX) {
break;
}
}
long casTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
System.out.println("Time Taken=" + casTime + "ms");
}
}
C code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NITER 500000000
int main (){
long val = 0;
clock_t starttime = clock ();
while (val < NITER){
while (1){
long current = val;
long next = current+1;
if ( __sync_bool_compare_and_swap (&val, current, next))
break;
}
}
clock_t castime = (clock()-starttime)/ (CLOCKS_PER_SEC / 1000);
printf ("Time taken : %d ",castime);
}
run.sh
#!/bin/bash
gcc -O3 test.c -o test.o
echo -e "\nC"
./test.o
javac SmallerCASTest.java
echo -e "\nJava"
java SmallerCASTest
Other details:
System : Linux XXXXXXXXX #1 SMP Thu Mar 22 08:00:08 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
gcc --version:
gcc (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
java -version:
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode)
You are comparing apples with oranges as I am sure you expected. The java version is a true CAS with retry on failure while the C version is using what I'd call in java a synchronized form.
See this question for more details.
See this answer to that question for supporting narrative where it says A full memory barrier is created when this function is invoked, i.e. in java terms, this is a synchronized call.
Try using _compare_and_swap in the same way AtomicLong uses its java equivalent, i.e. spin on the function until the value changes to what you want it to be.
Added:
I cannot find a definitive C++ equivalent of a java AtomicLong but that does not mean there isn't one. Essentially, an AtomicLong can be changed by any thread at any time and just one of them succeeds. However, the change will be consistent, i.e. the change will be the result of the change by one or other of the threads, it will not be a combination of the two. If thread A attempts to change the value to 0xffff0000 (or the equivalent 64bit number) while thread B attempts a change to 0x0000ffff (ditto) the result will be either of the two values, more specifically it will not be 0x00000000 or 0xffffffff (unless of course a third thread gets involved).
Essentially, an AtomicLong has no synchronisation at all other than this.
EDIT Indeed, java seems to implement incrementAndGet using a CAS operation, as you point out.
My testing seems to suggest that the C and Java versions have roughly equivalent performance (which makes sense, as the time consuming part is the atomic rather than any optimization of the rest that the java or C compilers manage to do).
So on my machine (Xeon X3450), the java version takes ~4700 ms, the C version ~4600 ms, a C version using __sync_add_and_fetch() ~3800 ms (suggesting that java could be improved here instead of implementing all the atomic operations on top of CAS).
java version is
java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.4) (6b24-1.11.4-1ubuntu0.10.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
GCC is 4.4.3, x86_64.
OS is Ubuntu 10.04 x86_64.
So I can only conclude that something seems fishy in your tests.
Because Java is awesome?
The java version takes 4ns for each loop. That is about right. An uncontended CAS is actually a CPU local operation, it should be very fast. (edit: probably not 4ns fast!)
Java achieves that speed by aggressive runtime optimization, the code is inlined and becomes just a couple of machine instructions, i.e. as fast as one can hand-code in assembly.
If the gcc version couldn't inline the function call, that's a big overhead per loop.
For the below given code , I see lot of GC activity. As per my understanding this is a suitable scenario for EA. Why EA is not effective. DummyObject has nothing allocated inside it. JVM options used : -server , -verbosegc.
static void anayzeEA()
{
for(int i = 0 ; i < 100000000; i++) {
DummyObject obj = new DummyObject();
if(obj.hashCode() == 97787) { //to prevent the obj being optimized
System.out.println(obj.hashCode());
}
}
}
See related Q&A here which suggests you can download a debug JDK and use command line options:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintEscapeAnalysis -XX:+PrintEliminateAllocations
to print out the escape analysis events as they happen.
Some observations
It seems like the obj.hashCode() is a native call and the object may escape . Changing obj.hashCode() to obj.getMyCode() (a method which returns the System.currentTimeMillis()% staticObjCount) made it work . No GC activity observed. However following method never got escape analysis in effect with all the suggestions mentioned
here
public static long test1()
{
long r = 0;
byte[] arr = new byte[(int)System.currentTimeMillis() % 1024];
if(arr.length == 998 ) {
++r;
}
return r;
}
JVM options used
-server
-verbosegc
-XX:CompileThreshold=1
Test1 is called over a number of times . Same old story. Java allocates memory in heap, GC comes and makes everything slow. was too excited about this feature.
Java API says:
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
So you are generating objects, producing non-predictable hashCodes for each and compare them to some value. Additional it's a native method, so the JIT doesn't know what's happening inside.
Escape Analysis may be good, but there's currently no support for crystal balls. ;-)
Try overriding it with your own method, returning 12345.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I wonder if anyone uses commercial/free java obfuscators on his own commercial product. I know only about one project that actually had an obfuscating step in the ant build step for releases.
Do you obfuscate? And if so, why do you obfuscate?
Is it really a way to protect the code or is it just a better feeling for the developers/managers?
edit: Ok, I to be exact about my point: Do you obfuscate to protect your IP (your algorithms, the work you've put into your product)? I won't obfuscate for security reasons, that doesn't feel right. So I'm only talking about protecting your applications code against competitors.
#staffan has a good point:
The reason to stay away from chaining code flow is that some of those changes makes it impossible for the JVM to efficiently optimize the code. In effect it will actually degrade the performance of your application.
If you do obfuscate, stay away from obfuscators that modify the code by changing code flow and/or adding exception blocks and such to make it hard to disassemble it. To make the code unreadable it is usually enough to just change all names of methods, fields and classes.
The reason to stay away from changing code flow is that some of those changes makes it impossible for the JVM to efficiently optimize the code. In effect it will actually degrade the performance of your application.
I think that the old (classical) way of the obfuscation is gradually losing its relevance. Because in most cases a classical obfuscators breaking a stack trace (it is not good for support your clients)
Nowadays the main point to not protect some algorithms, but to protect a sensitive data: API logins/passwords/keys, code which responsible for licensing (piracy still here, especially Western Europe, Russia, Asia, IMHO), advertisement account IDs, etc.
Interesting fact: we have all this sensitive data in Strings. Actually Strings is about 50-80% of logic of our applications.
It seems to me that future of obfuscation is "String encryption tools".
But now "String encryption" feature is available only in commercial obfuscators, such as: Allatori, Zelix KlassMaster, Smokescreen, Stringer Java Obfuscation Toolkit, DashO.
N.B.
I'm CEO at Licel LLC. Developer of Stringer Java Obfuscator.
I use proguard for JavaME development. It's not only very very good at making jar files smaller (Essential for mobile) but it is useful as a nicer way of doing device-specific code without resorting to IDE-unfriendly preprocessing tools such as antenna.
E.g.
public void doSomething()
{
/* Generated config class containing static finals: */
if (Configuration.ISMOTOROLA)
{
System.out.println("This is a motorola phone");
}
else
{
System.out.println("This is not a motorola phone");
}
}
This gets compiled, obfuscated, and the class file ends up as though you had written:
public void doSomething()
{
System.out.println("This is a motorola phone");
}
So you can have variants of code to work around manufacturer bugs in JVM/library implementations without bulking out the final executable class files.
I believe that some commercial obfuscators can also merge class files together in certain cases. This is useful because the more classes you have, the larger the size overhead you have in the zip (jar) file.
I spent some time this year trying out various Java obfuscators, and I found one to be miles ahead of the rest: JBCO. It's unfortunately a bit cumbersome to set up, and has no GUI, but in terms of the level of obfuscation it produces, it is unparalleled. You try feeding it a simple loop, and if your decompiler doesn't crash trying to load it, you will see something like this:
if(i < ll1) goto _L6; else goto _L5
_L5:
char ac[] = run(stop(lI1l));
l7 = (long)ac.length << 32 & 0xffffffff00000000L ^ l7 & 0xffffffffL;
if((int)((l7 & 0xffffffff00000000L) >> 32) != $5$)
{
l = (long)III << 50 & 0x4000000000000L ^ l & 0xfffbffffffffffffL;
} else
{
for(l3 = (long)III & 0xffffffffL ^ l3 & 0xffffffff00000000L; (int)(l3 & 0xffffffffL) < ll1; l3 = (long)(S$$ + (int)(l3 & 0xffffffffL)) ^ l3 & 0xffffffff00000000L)
{
for(int j = III; j < ll1; j++)
{
l2 = (long)actionevent[j][(int)(l3 & 0xffffffffL)] & 65535L ^ l2 & 0xffffffffffff0000L;
l6 = (long)(j << -351) & 0xffffffffL ^ l6 & 0xffffffff00000000L;
l1 = (long)((int)(l6 & 0xffffffffL) + j) & 0xffffffffL ^ l1 & 0xffffffff00000000L;
l = (long)((int)(l1 & 0xffffffffL) + (int)(l3 & 0xffffffffL)) << 16 & 0xffffffff0000L ^ l & 0xffff00000000ffffL;
l = (long)ac[(int)((l & 0xffffffff0000L) >> 16)] & 65535L ^ l & 0xffffffffffff0000L;
if((char)(int)(l2 & 65535L) != (char)(int)(l & 65535L))
{
l = (long)III << 50 & 0x4000000000000L ^ l & 0xfffbffffffffffffL;
}
}
}
}
You didn't know Java had goto's? Well, the JVM supports them =)
I use ProGuard and highly recommend it. While obfuscation does protect your code from casual attackers, it's main benefit is the minimizing effect of removing unused classes and methods and shortening all identifiers to 1 or 2 characters.
I think that for the most part obfuscation is pointless: even with full source code it's generally hard enough to figure out what the heck intention was (assuming there are no comments, and no meaningful names for local variables -- which is the case when re-generating sources from byte code). Obfuscation just decorates the cake.
I think developers and especially their managers tend to greatly over-exaggerate risk of someone seeing the source code. While good decompilers can produce nice looking source code, it's not trivial to work with it, and costs associated (not to mention legal risks) are high enough to make this approach seldom useful. I have only decompiled to debug problems with closed-source vendors' products (deadlocks in DB abstraction layer, ugh).
Bytecode was actually obfuscated, I think, but we nonetheless found the underlying problem -- it was an actual design problem.
I guess it really comes down to what your Java code is for, how it's distributed and who your clients are. We don't obfuscate anything, as we've never found one that was particularly good and it tends to be more trouble than it's worth. If someone has access to our JAR files and has the knowledge to be able to sniff around inside them, then there's far more worrying things that they can do than rip off our source code.