- it allows polyglot development
- it allows Ahead-of-Time (AOT) compilation.
Sunday, June 15, 2025
A Quick look at Quarkus
Monday, February 3, 2025
NVidia Drivers on Linux
sudo apt install linux-headers-$(uname -r)Saturday, October 26, 2024
NVIDIA Rapids
...
at ai.rapids.cudf.NativeDepsLoader.loadDep(NativeDepsLoader.java:246)
yum -y install git
"cudaFree(0) to actually allocate the set device - no process exclusive required since we are relying on Spark to schedule it properly and not give it to multiple executors"
Wednesday, October 25, 2023
Java and the GPU
Java was always built to abstract away the hardware on which it runs but it's approach to GPU has been somewhat late to the game [Gary Frost, YouTube].
There are projects out there that promise to give Java access to the GPU. I looked at Aparapi but it appears to be moribud. So, I gravitated to TornadoVM which Frost describes as "state of the art".
The trouble is that TornadoVM runs everything in a Docker image that has all the shared objects built in. This is fine for a quick demo - this is the result of running on my Quadro T2000:
docker-tornadovm$ ./run_nvidia_openjdk.sh tornado -cp example/target/example-1.0-SNAPSHOT.jar example.MatrixMultiplication
Computing MxM of 512x512
CPU Execution: 1.17 GFlops, Total time = 230 ms
GPU Execution: 268.44 GFlops, Total Time = 1 ms
Speedup: 230x
To work this out, I opened a shell in the Docker image and saw that the TornadoVM build it uses was built from Git branch d3062accc. So, the first thing was to checkout that branch of TornadoVM and build it.
I built with:
mvn clean install -Pgraal-jdk-11-plus
using the graalvm-ee-java11-22.3.4 JDK.
Note that you'll need Graal as the TornadoVM code has dependencies on it. I built my own Graal JDK by following the instructions here but using a different branch as I couldn't find the download for the graal.version defined in the TornadoVM pom.xml. Note, you'll also need mx and a bootstrapping JDK that has the right compiler interface (JVMCI), in my case labsjdk-ce-21.0.1-jvmci-23.1-b19.
So far, so good. I ran the tornado script which is just a wrapper around a call to the java executable (don't forget to set your JAVA_HOME environment variable to point at the Graal JDK) but it complained it could not see a tornado.backend file.
Again, a sneaky look at the Docker container indicated that we have to tell it which driver to use. So, I created the file and told it tornado.backends=opencl-backend but then tornado complained it didn't have the OpenCL drivers. Oops.
You have to build the drivers you want seperately it seems. But if you try to build Tornado drivers without the native OpenCL dev library, you'll see:
TornadoVM/tornado-drivers/opencl-jni$ mvn clean install # yes, Maven cmake via cmake-maven-plugin
....
/usr/bin/ld: cannot find -lOpenCL
...
The Docker image saves you from having to install the OpenCL libraries on your machine. To get it working on bare metal, I played it safe and got an old Ubuntu box and installed them there. You'll need to install them with:
sudo apt install ocl-icd-opencl-dev
However, running tornado in the subsequent dist folder still pukes but with something like:
Now, finally, you can run on the bare metal:
Note, you'll need to also run it with the Graal JVM. Set both the PATH and JAVA_HOME environment variables to point to it.
Where now?
This is a nice introduction to running Java on the GPU but it's just the start. There are many caveats. Example: what if your Java code throws an Exception? GPUs have no equivalent of exceptions so what happens then? More to come.
Monday, June 6, 2022
Packaging Python
Java programmers don't know the meaning of classpath hell until they've played with Python. Here are some notes I took while ploughing through the excellent Practical MLOps (Gift & Deza). Following their instructions, I as attempting to get a ML model served using Flask in a Docker container. Spoiler: it didn't work out of the box.
Since the correct OnnxRuntime wheel for my Python runtime did not exist, I had to build onnxruntime with --build-wheel while making the artifact.
This is where I encountered my first dependency horror:
CMake 3.18 or higher is required. You are running version 3.10.2
Wednesday, May 25, 2022
The CLI for busy Data Scientists and Engineers
I've been asked to give a talk on the command line interface for a mixed audience of Data Scientists and Engineers. Since the world is becoming ever more container based, I'm focussing on Linux.
Containers
Sometimes you need to diagnose things within the container. Jump into the container with:
docker exec -it CONTAINER_ID /bin/bash
To get the basic diagnostic tools mentioned below, you'll generally need to execute:
apt-get update # you need to run this first
apt-get install net-tools # gives you netstat
apt-get install iputils-ping # gives you ping
apt-get install procps # gives you ps
apt-get install lsof
Note these installations will all be gone next time you fire up the image as the underlying image does not change.
You can find out which module your favourite command belongs to by running something like this:
$ dpkg -S /bin/netstat
net-tools: /bin/netstat
Formatting
You can use regex expressions in grep with the -P switch. For example, let's search for lines that are strictly composed of two 5-letter words seperated by a space:
$ echo hello world | grep -P "\w{5}\s\w{5}$"
hello world
$ echo hello wordle | grep -P "\w{5}\s\w{5}$"
$
You can extract elements from a string with an arbitrary delimiter with awk. For example, this takes the first and sixth elements from a line of CSV:
$ echo this,is,a,line,of,csv | awk -F',' '{print $1 " " $6}'
this csv
$
To prettify output, use column like this:
$ echo "hello, world. I love you!
goodbye cruelest world! So sad" | column -t
hello, world. I love you!
goodbye cruelest world! So sad
$
To see what your filewall is dropping (useful when you've misconfigured a node), run:
$ sudo iptables -L -n -v -x
To see current network connections, run:
$ netstat -nap
You might want to pipe that to grep LISTEN to see what processes are listening and on which port. Very useful if something already has control of port 8080.
For threads, you can see what's going on by accessing the /proc directory. While threads are easy to see in Java (jstack), Python is a little more opaque, not least because the Global Interpretter Lock (GIL) only really allows one physical thread of execution (even if Python can allow logical threads). To utilise more processors, you must start a heavyweight thread (see "multiprocessing" here). Anyway, find the process ID you're interested in and run something like:
$ sudo cat /proc/YOUR_PROCESS_ID/stack
[<0>] do_wait+0x1cb/0x230
[<0>] kernel_wait4+0x89/0x130
[<0>] __do_sys_wait4+0x95/0xa0
[<0>] __x64_sys_wait4+0x1e/0x20
[<0>] do_syscall_64+0x57/0x190
[<0>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
If you want to pin work to certain cores, use something like taskset. For example, if I wanted to run COMMAND on all but one of my 16 cores, I run:
taskset 0xFFFE COMMAND
This is very useful if some data munging is so intense it's bringing my system down. Using this, at least one thread is left for the OS.
Finally, vmstat gives you lots of information about the health of the box such as blocks being read/written from/to disk (bo/bi), the number of processes runnable (not necessarily running) and the number blcoked (r/b) and the number of context switches per second (cs)
Saturday, May 23, 2020
When Penguins Attack
It's that moment that everybody who auto updates their laptops fears: it doesn't work. My problem was that after logging in, the OS paused briefly and then sent me back to the login screen. Hmm.
In this situation, get a CLI prompt with CTRL-ALT-F3. Since we managed to get a login prompt and then failed, it's probably the video drivers. I checked and saw a message similar to this (segmentation fault) in /var/log/Xorg.0.log
Foolishly, I chose to rollback a recent kernel update by updating /etc/default/grub. This is a matter of setting GRUB_DEFAULT to a number indicating how many steps back you want (0 obviously uses the most recent). Don't forget to run sudo update-grub when you're done.
This was foolish as I had rolled back to a kernel version that before a recent BIOS update. Now, things were much worse as my laptop could not get past the BIOS screen. Great. Even trying holding down shift at boot time to allow me change kernels didn't work.
If you've bought a Dell (I have a Precision 7540, an ugly but decent machine), you'll be given a System Service Tag on the the invoice they've sent you. Give Dell this ID here and download a recovery ISO. I tried phoning Dell Support but they never got back to me.
Create a bootable USB using Startup Disk Creator. Naturally, you'll need another Ubuntu machine to do this.
As it happens, this startup disk just tries to install another version of Ubuntu which blew a bit of disk space but there was no reason to do this. I just dropped to a CLI with CTRL-ALT-F3 and saw what hard disks there were with fdisk -l, identified the partition that was clearly mine (at over 800gb) and mounted it with:
mount /dev/MY_DISK /mnt/old
(this SO answer proved invaluable). Now I can read my old disk. Good.
Now, I can rollback the change I made to my grub file and sudo update-grub again. But first, I needed to chroot that mounted disk to pretend it's running as my root file system. Don't forget to also mount other important mount points to make it look genuine. This SO answer will help.
Rebooting got me back to my original problem of not being able to run XServer. The solution was: keep the kernel and rollback the graphics driver updates with a third SO answer here. For me, this was sudo apt-get purgeing all things NVIDIA and running sudo apt-get install nvidia-driver-430 (the driver version may be different for you).
Rebooting once more got my system back but c'mon you guys at Dell and Ubuntu - don't break my system. That's a few hours I won't get back.
Saturday, July 26, 2014
UDP and Full Buffers
Oracle's Coherence has a very nice reporting tool. If you don't have it running by default, you can connect via JConsole and turn it on. Give it a directory and it will print cluster statistics to it every minute.
The reason we need these stats is that on a quiet system, the percentage of packets dropped on some nodes can be terrible. I've been looking at why.
Oracle Coherence nodes talk to each other via UDP. Unlike TCP, nothing is ensuring our packets reach their destination. To counter this, Coherence builds its own protocol on top of UDP that resends packets if they have not been acknowledged or if the sender receives a NACK.
On a decent network, why would our packets not reach their destination? Well, one reason is that the receiving buffer is full. We've set our buffers at 2MB (per Oracle's advice) so this is unlikely but possible. If an application receives UDP packets faster than it can process them, packets will be dropped.
However, if you're running on Linux, you can easily see if this is the case.
henryp@phillsdell:~$ netstat -su
.
.
Udp:
3196 packets received
9 packets to unknown port received.
0 packet receive errors
2770 packets sent
Monday, May 26, 2014
Debugging the JVM
This is very nice. I've built my own JDK on Linux and am stepping through the C++ code as if it were a normal Java debug.
To do this:
1. Get the source code with:
hg clone http://hg.openjdk.java.net/jdk9/dev 9dev
$ make all CONF=linux-x86_64-normal-server-slowdebug ZIP_DEBUGINFO_FILES=0
gmake[2]: *** Waiting for unfinished jobs....
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
gmake[1]: *** [demos] Error 2
make: *** [demos-only] Error 2
GNU gdb (GDB) Fedora (7.4.50.20120120-54.fc17)
.
.
[henryp@corsair 9dev]$ gcc --version
gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)
.
.
Monday, May 12, 2014
How do you distinguish a slow application from a slow network?
We've been given new hardware on which to run our application. To make sure it will be OK, we ran our performance tests against it. To our surprise, they sometimes did poorly despite the new CPUs being much better. Using jstack showed at any particular time, nothing much was going on.
First we looked at playing with OS parameters but we had no luck. Then we looked at esoteric things like traffic shaping but found nothing.
So, time to crack open Wireshark. We took a 60s sample of network traffic when things were going well as a baseline then sampled for 60s during slowness.
Although the application's functionality was fine during both samples, the TCP/IP traffic showed big differences. The slow traffic had lots of re-transmissions and "TCP ACKed lost segment" messages in the log.
To filter them in Wireshark, use:
tcp.analysis.lost_segment || tcp.analysis.retransmission
(Incidentally, another interesting filter is:
frame.time_delta > 0.1
which shows packets that had a gap between them of 0.1s - which is ages in the traffic world. Another is
tcp.window_size == 0
which shows your buffers filling up)
In our 60s capture on the client side, there were some 2 million frames captured. Of these, some 83 000 we re-transmissions or lost segments. This is 4% of our traffic and seems enough to slow our connections noticeably.
We've been assured by the hardware guys everything is fine. But we do share a VLAN (that is, we share physical networks with other teams) and that may be exhausted. This piece of kit is a Cisco Nexus 5k.
The mystery continues.
Saturday, May 10, 2014
CPU affinity and Performance
I have a single-threaded application (my play-project, JStringServer, running BatchedServerMain) that is very CPU and IO heavy and I want to improve its performance and stop it from jittering.
By running htop, I notice that it jumps from CPU to CPU. One moment it's on CPU 6...
the next, it's on CPU 11:
The number of serviced clients per second is all over the place (a mean of 9523 per second and a standard deviation of 864. Compare this with the results below).
The Solution
You might have read a lot about CPU affinity on the blogosphere (such as HighScalability and Mechanical Sympathy). Jumping between CPUs can be expensive and one tactic is to avoid it. Could it help me?
I've heard of proposals to allow this to be done within Java but there is nothing I know of that allows you to do it yet so I will have to do it via the OS.
CPU Affinity
You can pin processes to given CPUs quite easily on Linux (see this article for instructions). But which do we pin it to? Not all are the same...
I pin my process to each CPU and take timings by hitting the process from another box. These are the results I get measuring the number of clients serviced each second:
0 : mean = 10244.200 std dev = 47.298
1 : mean = 10253.600 std dev = 52.063
2 : mean = 10245.400 std dev = 48.202
3 : mean = 10204.800 std dev = 47.422
4 : mean = 10265.000 std dev = 54.902
5 : mean = 7807.400 std dev = 41.105
6 : mean = 10217.400 std dev = 51.623
7 : mean = 10274.000 std dev = 46.656
8 : mean = 10238.600 std dev = 62.919
9 : mean = 10290.600 std dev = 49.318
10 : mean = 10209.400 std dev = 52.044
11 : mean = 10194.800 std dev = 54.377
12 : mean = 10243.400 std dev = 52.321
13 : mean = 8874.000 std dev = 43.959
14 : mean = 9849.200 std dev = 49.794
15 : mean = 10352.000 std dev = 55.194
Hmm, three CPUs are slow, but especially CPU #5.
Let's look at my system. I'm using an Intel Xeon processor (E5-2687W) which has 8 cores and 16 hardware threads.
[henryp@corsair ~]$ numactl -H
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 65480 MB
node 0 free: 60931 MB
node distances:
node 0
0: 10
This shows I have only one CPU and memory nodes so I don't have to worry about cross-socket NUMA problems (This is a good article about looking at NUMA and hardware).
OK, so what are all my CPUs in my socket doing?
[henryp@corsair 9dev]$ cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
0: 171 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-edge timer
1: 3644 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-edge i8042
4: 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-edge
8: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-edge rtc0
9: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-fasteoi acpi
12: 132544 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-edge i8042
17: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-fasteoi firewire_ohci
23: 61 0 0 0 0 0 21 0 0 0 0 0 0 0 0 0 IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2
27: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-fasteoi ath9k
32: 207183 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-fasteoi nvidia
36: 179 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IO-APIC-fasteoi snd_hda_intel
64: 27439 25657 81300 0 6288 32731 5618 45423 0 0 0 0 0 0 0 0 PCI-MSI-edge ahci
65: 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge ahci
66: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
67: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
68: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
69: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
70: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
71: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
72: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
73: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
74: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
75: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
76: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
77: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
78: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
79: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
80: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
81: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd
82: 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge mei
83: 1576 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge em1
84: 757 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge snd_hda_intel
85: 14 0 0 0 0 424447 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge p7p1-rx-0
86: 19 0 0 0 0 0 216701 0 0 0 0 0 0 0 0 0 PCI-MSI-edge p7p1-tx-0
87: 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PCI-MSI-edge p7p1
NMI: 332 254 231 207 211 310 218 219 403 303 194 228 280 382 329 281 Non-maskable interrupts
LOC: 418851 409978 386151 360594 358830 438636 351885 362193 292543 243019 179805 188224 223758 283426 257005 225531 Local timer interrupts
SPU: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Spurious interrupts
PMI: 332 254 231 207 211 310 218 219 403 303 194 228 280 382 329 281 Performance monitoring interrupts
IWI: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IRQ work interrupts
RTR: 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 APIC ICR read retries
RES: 1069726 92676 20752 8650 5003 4081 3523 2631 2234 2171 2467 1658 1974 1873 1849 1668 Rescheduling interrupts
CAL: 53667 72109 69200 68746 62293 82125 72462 71374 49909 51974 52292 54965 55904 52059 54509 54112 Function call interrupts
TLB: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 TLB shootdowns
TRM: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Thermal event interrupts
THR: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Threshold APIC interrupts
MCE: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Machine check exceptions
MCP: 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 Machine check polls
ERR: 0
MIS: 0
CPU #5 seems to be handling an awful lot of interrupts from the network interface controller [NIC] for p7p1 (the name of my interface that the client is hitting).
"Unfortunately the default is to let all processors handle interrupts with the result that the overall performance is not optimal in particular on multi-core systems. This is because modern NICs can have multiple RX queues that work perfectly if cache coherency is respected. This means that interrupts for ethX-RX queue Y must be sent to 1 (one) core or at most to a core and its Hyper-Threaded (HT) companion. If multiple processors handle the same RX queue, this invalidates the cache and performance gets worse. For this reason IRQ balancing is the key for performance." [1]
OK, let's balance that puppy.
[root@corsair ~]# echo 15 > /proc/irq/85/smp_affinity ; echo 15 > /proc/irq/85/smp_affinity_list
So, I use Tuna to see what's going on. This is how it looks before the test:
And during:
We can see CPU #5 is handling a lot of kernel interrupts.
Conclusion
It seems that this particular CPU is busy handling network IO and making my process perform badly if I pin it to it. This is probably a reason for the jitter as the kernel sometimes schedules my process to run on it.
But at the moment, I don't know how to stop the kernel from disabling network interrupts on this CPU. So, I'll just avoid it in the future.
[1] http://www.ntop.org/pf_ring/irq-balancing/




