{"id":21149432,"url":"https://github.com/xemantic/java-2-times-faster-than-c","last_synced_at":"2025-07-09T09:31:05.011Z","repository":{"id":54632193,"uuid":"330790563","full_name":"xemantic/java-2-times-faster-than-c","owner":"xemantic","description":"An inquiry into nondogmatic software development. An experiment showing double performance of the code running on JVM comparing to equivalent native C code.","archived":false,"fork":false,"pushed_at":"2021-03-21T13:12:22.000Z","size":218,"stargazers_count":50,"open_issues_count":5,"forks_count":3,"subscribers_count":7,"default_branch":"main","last_synced_at":"2024-05-01T11:38:58.660Z","etag":null,"topics":["benchmark","bytecode","c","compiler","java","jvm","language-comparison","performance","runtime","virtual-machine","vm","vm-language"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xemantic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-18T21:25:27.000Z","updated_at":"2024-01-19T10:14:04.000Z","dependencies_parsed_at":"2022-08-13T22:10:19.617Z","dependency_job_id":null,"html_url":"https://github.com/xemantic/java-2-times-faster-than-c","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fjava-2-times-faster-than-c","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fjava-2-times-faster-than-c/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fjava-2-times-faster-than-c/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xemantic%2Fjava-2-times-faster-than-c/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xemantic","download_url":"https://codeload.github.com/xemantic/java-2-times-faster-than-c/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225521909,"owners_count":17483515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmark","bytecode","c","compiler","java","jvm","language-comparison","performance","runtime","virtual-machine","vm","vm-language"],"created_at":"2024-11-20T09:35:47.426Z","updated_at":"2024-11-20T09:35:47.936Z","avatar_url":"https://github.com/xemantic.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# java-2-times-faster-than-c\n_An experiment showing double performance of the code running on JVM comparing to equivalent native C code_\n\n![artwork](https://repository-images.githubusercontent.com/330790563/c56fa080-5a76-11eb-81d2-515c4cc4b42d)\n\n:warning: The title of this project is provocative, and it is meant to be, to bring\nattention to certain ideas. Please read through this document before jumping to any conclusions.\nFor now, I will just say that the title applies just to the algorithm presented here, not to\nJava and C in general. I am also the farthest away from convincing anyone to choose Java\nover any other language, and I even see good reasons to discourage you from using Java in\nplethora of cases. I rarely code Java myself these days.\n\n\n## My typical dialog from the past days\n\n\u003e \"Your code running on a virtual machine will be ALWAYS slower than equivalent native code.\"\n\u003e \n\u003e \"Why?\"\n\u003e \n\u003e \"Because of automatic memory management.\"\n\u003e \n\u003e \"Why is it so?\"\n\u003e \n\u003e \"Things like automatic memory management ALWAYS add additional overhead to execution.\"\n\u003e \n\u003e \"Hmm, let me try, here is a code in Java, and direct equivalent in C,\n\u003e the first one is almost 2 times faster.\"\n\u003e \n\u003e \"It's because you are doing things wrong. No one would write C code like this.\"\n\u003e \n\u003e \"Why?\"\n\u003e \n\u003e \"Because you need to properly manage your memory for efficiency.\"\n\u003e \n\u003e \"How do you do it?\"\n\u003e \n\u003e \"Depending on your problem, sometimes even by adding automatic memory management.\"\n\u003e \n\u003e \"Ok, so did you just make contradictory statements?\"\n\u003e \n\u003e \"I don't think so, just add these few lines to your code.\"\n\u003e \n\u003e \"Do you think it's still the same algorithm afterwards?\"\n\u003e \n\u003e \"Yeah.\"\n\u003e \n\u003e \"But is your memory management solution adjusted to this specific C code and\n\u003e therefore extending the algorithm?\"\n\u003e \n\u003e \"Yeah.\"\n\u003e \n\u003e \"So it's no longer algorithmically equivalent code, isn't it?\"\n\u003e \n\u003e \"Yeah.\"\n\u003e \n\u003e \"Did you just make contradictory statements again?\"\n\u003e \n\u003e \"I don't think so.\"\n\n\n## Show me the code\n\nThe code is almost the same in both languages, still using typical conventions of both of them:\n\n* [java_2_times_faster_than_c.c](src/main/c/java_2_times_faster_than_c.c)\n* [Java2TimesFasterThanC.java](src/main/java/com/xemantic/test/howfast/Java2TimesFasterThanC.java)\n\nI am pretty convinced that algorithmically they are equal, except for obvious explicit memory\nreleasing in C version. Here is an\n[old but comprehensive article](https://www.ibm.com/developerworks/java/library/j-jtp09275/index.html)\nshedding some light on my results.\n\nI haven't written any C code for 2 decades, and it was nice to write some now, to rediscover how\nclose and influenced Java actually is by C, and how it is designed to run surprisingly close\nto the hardware (primitive data types).\n\nThe code is establishing a ring of nodes first, and then mutating it continuously while\ndeleting nodes in one direction and inserting them in another.\nThe number of inserted and deleted nodes is unpredictable. The same applies to the size\nof a node. Still the pseudo random distribution will be exactly the same for Java and C.\n\nTo achieve this, I took deterministic, almost random distribution I often use in GLSL,\nwhich I borrowed from [The Book of Shaders](https://thebookofshaders.com/). I also wrote\na benchmark for this one:\n\n* [almost_pseudo_random.c](src/main/c/almost_pseudo_random.c)\n* [AlmostPseudoRandom](src/main/java/com/xemantic/test/howfast/AlmostPseudoRandom.java)\n\nI was expecting this time C code to be 2 times faster, but to my surprise Java version is faster\nagain (although not 2 times), which I cannot explain. I have many hypothesis:\n\n* HotSpot is doing some aggressive inlining possible after the running code is analyzed for a while.\n* C math functions are from the library, so maybe they actually cannot be inlined, while HotSpot\n  has the freedom of inlining whatever it pleases.\n* Unlike C, Java allows using the `%` operator also for floating point numbers. It might be mapped\n  onto more effective machine code.\n\nPlease feel free to disassemble the code and create PR with proper explanation. It is also\npossible to dump assembly running on JVM:\n\nhttps://wiki.openjdk.java.net/display/HotSpot/PrintAssembly\n\n\n## Speeding up C version\n\nMy example is pushing things to absurd, for a reason. Of course it is possible to outperform\nJava version by managing memory better in C. But it would imply embedding additional algorithms\nof memory management into my original code, therefore I wouldn't call it \"equivalent\" anymore\nin algorithmic sense, because memory allocation, and releasing it implicitly or explicitly,\nis a crucial part of this algorithm.\n\nWhile saying that, I've received amazing feedback showing me how to achieve extremely efficient\nmemory management in C, for example in ticket [#1](../../issues/1), and I am grateful for this\ncontribution and opportunity to learn. Therefore I would like to include also extra version of this\nalgorithm in C, but with more efficient memory management, also taking variable size of data\nstructures into account. Unfortunately my limited C experience does not allow me at this point to\nwrite it myself. :( If you feel up for this challenge, please contribute to this project.\n\nAnd here is my hypothesis:\n\n**Certain classes of algorithms can gain extra performance just thanks to the fact of being\nexpressed in the language assuming automatic memory management.**\n\nMy experience of writing complex distributed systems, and also my intuition, tells me that\nthese algorithms are pretty common, and in the same time I have a feeling that these cases are\nrarely covered in microbenchmarks comparing speed of the code expressed and compiled in\ndifferent languages. If there is a minimal thing I want to achieve with this experiment, it is to\nconvince myself and others, to always question certain dogmas of modern software development and\nvalidity of certain arguments in given context. Please check issue [#2](../../issues/2) as an\nexemplum of what I am referring to.\n\n\n## Does it have any practical implications?\n\nIt does not, except from methodological perspective it seems to falsify certain statements with \ngeneralized quantifiers. So it becomes rather something like:\n\n\"Your algorithm written in code designed to run on a virtual machine will be usually slower than\nequivalent native code.\"\n\n\"always\", becomes \"usually\", and \"usually\" implies that from now on we should rather revalidate for\neach case than make categorical statements.\n\nCommon wisdom from microbenchmarks is usually showing JVM to be around 10%-20% percent\nslower than the equivalent optimized native code, with big outliers in favor of the\nnative code. My simplest microbenchmark with `almost pseudo random` is showing something\nopposite, but I wouldn't jump to any conclusion from it.\n\nBut how do these tests actually compare to real life code where most of the software is\nusing data structures already standardized for each language?\nI don't know of any benchmarks which can objectively, in generic way, measure the effects which I\nam describing here. I don't believe that my experiment can be really scaled. But I believe that the\npower of certain algorithmic expressiveness improves the performance\ninstead of degrading it, like in case of [reactive programming](https://en.wikipedia.org/wiki/Reactive_programming)\nparadigm. Of course by principle, at the end of the day, everything can be reduced to machine code,\nwhich can be optimized to extreme in assembly code. But would it be really algorithmically\nequivalent? I will leave this question open.\n\nIn distributed systems powering the dynamic internet services we are using, the CPU based\nperformance is usually impacted by IO throughput. Overall performance improvement will come not\nfrom the fact that our code is faster, but from the fact that we are waiting \"better\",\nwith less contention, while collecting the garbage in parallel, and while preventing the whole\nsystem from entering the [thrashing state](https://en.wikipedia.org/wiki/Thrashing_(computer_science))\nwith tools like [circuit breakers](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).\nIf your code represents typical dynamic web stack, with data retrieved from the database, possibly\nstreamed, and converted to JSON on the fly, each request typically involves myriads of new data\ninstances of unpredictable size in the pipeline, which are immediately discarded at the end of the\nrequest. The goal is to minimize the response time and virtual machines seem to greatly contribute\nto that.\n\n\n## Myths and Urban Legends of modern computing :)\n\nJust to recapitulate the myths associated with virtual machines and automatic memory management:\n\n * code executing on VM is ALWAYS slower than the native one\n * garbage collection is ALWAYS harming the performance\n * garbage collection is causing \"stop the world\"\n\nNone of these seem to be true these days:\n\n * it seems that the code executing on VM can be actually quite optimal thanks to\n   technologies like [HotSpot](https://en.wikipedia.org/wiki/HotSpot_(virtual_machine))\n   which even my simplest benchmark shows.\n * garbage collection can actually greatly improve the performance of common algorithms\n * on JVM GC is mostly happening as a parallel operation these days\n\n\n## Should I rewrite all my code in Java now?\n\nAbsolutely not!!! Performance is not the only reason why we are choosing given language.\nWhen I started coding in JDK 1.0.2 (the first stable release), it was 20 times slower than\nthe native code, but the Java code I compiled back then in 1997 still just runs on the\nnewest JVM of Java 15. I cannot say the same about the code from this time written in Pascal,\nAssembler, C, C++. The promise\n\"[Write once, run anywhere](https://en.wikipedia.org/wiki/Write_once,_run_anywhere)\", given me by\nlegendary [Sun Microsystems](https://en.wikipedia.org/wiki/Sun_Microsystems),\nwas kept while the whole runtime and toolchain became open source. This is the actual\nsuperpower of Java I want to pay tribute to - it has been helping me in building\ncomplex software systems for years, with the speed of great toolchain of remote debuggers,\nstatistical profilers and incremental compilers inherent to the design of the language from equally\nlegendary [James Gosling](https://en.wikipedia.org/wiki/James_Gosling).\n\nBut I also want to pay a tribute to C, which is powering the Linux kernel - the operating system\nwe are using every day, even if we are not aware of it. It might be in our Android phone\nor tablet, router, and all the servers on the way passing the signal from one human brain\nto another. Even git itself, the tool managing source code of this project, is written in C.\nAnd all of it thanks to the charisma of one person -\n[Linus Torvalds](https://en.wikipedia.org/wiki/Linus_Torvalds).\n\nI rarely code Java myself these days, rather GLSL, JavaScript, HTML, CSS and Kotlin, the last one\nstill usually running on JVM, although with JavaScript and native as possible compilation\ntargets. My IDE runs on JVM as well. Sometimes I transpile Java to JavaScript. Sometimes\nI transpile JavaScript to JavaScript. There are plenty of other possible reasons why you\nshouldn't use Java:\n\n * You are proficient in another language.\n * You prefer pure functional programming.\n * JVM-based solutions tend to have higher memory footprint which is disqualifying it in many\n   embedded systems.\n * For the code relying mostly on GPU performance gains on CPU might be neglectable.\n * etc.\n\n**But if your solution requires a cluster of 100 servers behind load balancer, then you can maybe\nimprove average response time from 100ms to 50ms on the same virtual hardware while safely shutting\ndown half of these machines? It might cut enough Amazon data center costs to hire\n2 or 3 more developers :)**\n\nI did this for several organizations in the past, while always improving the performance of the\nstack by order of magnitude.\n\nI'm not a big fan of microbenchmarks and language comparisons which are often biased and\nmisleading without the context, therefore fueling \"holy crusades\" and \"genital measurement contests\".\nBut I'm a natural born iconoclast, always eager to compare the myth with the reality. And \nin reality you will often hear \"arguments from performance\" which are equally often\nirrelevant to the context they are expressed in. Language is just a tool. Spoken is often\ncherished on the altar of national ideology and computer ones are often becoming a fetish\nof our idiosyncrasy which we impose on the others. We can do better. I write WE, because\nobviously I am also not free from these tendencies. :)\n\nFrom my experience of leading brilliant software teams I've learned that the actual quality\nchange in performance does not come from particular technology, but rather from the paradigm\nshift in the architecture of the whole system. Technologies like JVM might be a tool of\nimprovement, but they can also be misused terribly.\n\n\n## Build\n\nIn the project dir:\n\n```console\n$ ./build-gcc.sh\n$ ./build-clang.sh\n$ ./gradlew build\n```\n\n\n## Running\n\nHere are tests results from my machine:\n\n```console\n$ time ./build/clang/java_2_times_faster_than_c \nnode count: 13537\nchecksum: 470936697371\n\nreal    0m12,726s\nuser    0m12,719s\nsys     0m0,004s\n$ time ./build/gcc/java_2_times_faster_than_c \nnode count: 13537\nchecksum: 470936697371\n\nreal    0m12,800s\nuser    0m12,795s\nsys     0m0,004s\n$ time java -cp build/classes/java/main com.xemantic.test.howfast.Java2TimesFasterThanC \nnode count: 13537\nchecksum: 470936697371\n\nreal\t0m8,569s\nuser\t0m8,701s\nsys\t0m0,117s\n```\n\n```console\n$ time ./build/gcc/almost_pseudo_random \nchecksum: 499999997.122350\n\nreal    1m4,433s\nuser    1m4,424s\nsys     0m0,008s\n$ time ./build/clang/almost_pseudo_random \nchecksum: 499999997.122350\n\nreal    1m4,878s\nuser    1m4,877s\nsys     0m0,000s\n$ time java -cp build/classes/java/main com.xemantic.test.howfast.AlmostPseudoRandom \nchecksum: 4.9999999712235045E8\n\nreal    0m51,235s\nuser    0m51,193s\nsys     0m0,056s\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to see the specs of my machine\u003c/summary\u003e\n\u003cp\u003e\n\n```console\n$ cat /proc/cpuinfo \nprocessor\t: 0\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 142\nmodel name\t: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz\nstepping\t: 10\nmicrocode\t: 0xe0\ncpu MHz\t\t: 700.046\ncache size\t: 8192 KB\nphysical id\t: 0\nsiblings\t: 8\ncore id\t\t: 0\ncpu cores\t: 4\napicid\t\t: 0\ninitial apicid\t: 0\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d\nvmx flags\t: vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple pml ept_mode_based_exec\nbugs\t\t: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds\nbogomips\t: 3999.93\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 39 bits physical, 48 bits virtual\npower management:\n\n```\nx8 cores\n\n```console\n$ cat /proc/meminfo \nMemTotal:       16102660 kB\nMemFree:          710648 kB\nMemAvailable:    4814532 kB\n// ...\n```\n\n\u003c/p\u003e\n\u003c/details\u003e \n\n\n## Contributions\n\n### C# version\n\nContributed by [hercegyu](https://github.com/hercegyu) \n\n```console\n$ wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb\n$ sudo dpkg -i packages-microsoft-prod.deb\n$ sudo apt-get update\n$ sudo apt-get install dotnet-sdk-5.0\n$ ./build-csharp\n$ time ./build/csharp/java-4-times-faster-than-c-sharp \nnode count: 13537\nchecksum: 470936697371\n\nreal    0m34,037s\nuser    0m36,997s\nsys     0m2,925s\n```\n\n\n### Go version\n\nContributed by [Elad Hirsch](https://github.com/eladh)\n\n```console\n$ sudo apt-get install golang-go\n$ ./build-go.sh\n$ time ./build/go/java_faster_than_go \nnode count: 13553\nchecksum:  486105193130\n\nreal    0m14,542s\nuser    0m18,274s\nsys     0m0,345s\n$ time build/go/almost_pseudo_random \nchecksum: 4.999999924931206e+08\n\nreal    0m28,191s\nuser    0m28,202s\nsys     0m0,009s\n```\n\n:information_source: Note that values slightly differ. Most likely it's because Go\nseems to have different implementation of trigonometric functions making the sequence\nof generated almost random numbers slightly different. It also seems that Go version\nof `almost pseudo random` test is 2 times faster than C and Java versions. This\ntest is only calling `sin(x)` in a loop.\n\n\n### JavaScript on node\n\nContributed by [Elad Hirsch](https://github.com/eladh)\n\n```console\n$ time node src/main/javascript/java_faster_than_javascript.js \nnode count: 13537\nchecksum: 470936697371\n\nreal    1m6,196s\nuser    1m13,707s\nsys     0m2,256s\n$ time node src/main/javascript/java_faster_than_node.js \nnode count: 13537\nchecksum: 470936697371\n\nreal    0m26,172s\nuser    0m30,301s\nsys     0m0,628s\n$ time node src/main/javascript/almost_pseudo_random.js \nchecksum: 499999997.12235045\n\nreal    2m13,332s\nuser    2m13,265s\nsys     0m0,060s\n```\n\n\n### Javascript in the browser\n\n:information_source: time in milliseconds\n\n#### Chrome\n\n* `java-faster-than-javascript.html`: `78857` - 1m19s\n* `almost-pseudo-random.html`: `186520` - 3min6s\n\n#### firefox\n\n* `java-faster-than-javascript.html`: `74803` - 1m14s\n* `almost-pseudo-random.html`: `84303` - 1m24s\n\n\n### Kotlin\n\nKotlin version has the same time characteristics as Java version when running on the same JVM.\n\n\n### Rust version\n\nContributed by [Sam Leonard](https://github.com/tritoke)\n\n```console\n$ time build/rust/rust_raw \nnode count: 13537\nchecksum: 470936697371\n\nreal    0m13,824s\nuser    0m13,819s\nsys     0m0,004s\n$ time build/rust/rust_safer \nnode count: 13537\nchecksum: 470936697371\n\nreal    0m13,801s\nuser    0m13,800s\nsys     0m0,001s\n$ time build/rust/almost_pseudo_random \nchecksum: 499999997.12235045\n\nreal    1m7,944s\nuser    1m7,938s\nsys     0m0,004s\n```\n\n## Future research and contributions\n\nI am looking for concise, deterministic, pseudorandom function, which is simple enough to have\ncomparable overhead when implemented in all the languages. The current `almostPseudoRandom`,\nbased on trigonometry, seems to be surprisingly expensive to compute and also gives slightly\ndifferent floating point results in Go version, where it is also 2 times faster than in Java and C.\n\nIn the future I will also use this project to test how Kotlin transpiles to native code and\nJavaScript.\n\nI will also rerun these tests from time to time, to check how runtimes and compilers evolve,\nbecause there are several ongoing efforts for improving memory management and garbage collection in\nmany of them.\n\nAny future contributions and optimizations of the current code are welcome. For example if you want\nto contribute a version with custom memory allocator, I would suggest to put it in a separate file,\ninstead of extending current code, for better overview how the optimization is achieved. Of course\nif there are any straightforward optimizations, or even justified style changes to the current\nexamples, feel free to improve them.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxemantic%2Fjava-2-times-faster-than-c","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxemantic%2Fjava-2-times-faster-than-c","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxemantic%2Fjava-2-times-faster-than-c/lists"}