It’s the first time I’ve gone non-Nvidia since before 2010 as I’ve been a Nvidia loyalist since the GeForce 256 back in 1999 (was 3dfx prior). But Nvidia GPU pricing on anything with decent VRAM has gotten absurd, even on the used market. I kept hearing that ROCm (Radeon Open Compute) support for inference has finally matured. So I decided to find out for myself.
I picked up the AMD Radeon AI PRO R9700, 32GB. Not cheap, but brand new its cheaper than an RTX 3090 24gb from 2020.
Since this is my first AMD in very long while, going to have to figure out drivers. So let’s get started. First lets gather some info by running the commands below.
lspci -nn | grep -Ei 'VGA|Display|AMD|ATI'
lsmod | grep amdgpu
dmesg | grep -i amdgpu | tail -50
Verify GPU Detection
Prior to card swap, purged my Ubuntu 24.04 system of all Nvidia drivers and tools.
Lets check to see if the card is detected properly
lspci -nn | grep -Ei 'VGA|Display|AMD|ATI'
This command searches the PCI device list for anything matching VGA, Display, AMD, or ATI. In the output below we see the following
03:00.0 is the PCI id of the R9700 GPU
Vendor ID 1002 confirms AMD Device
7551 is how the kernel identifies this GPU generation/device
The two PCI bridge entries show the GPU exposes an internal PCIe switch/bridge topology
01:00.0 PCI bridge: AMD Navi 10 XL Upstream Port of PCI Express Switch
02:00.0 PCI bridge: AMD Navi 10 XL Downstream Port of PCI Express Switch
The important takeaway is that Ubuntu already recognizes the R9700 correctly and the in-kernel AMDGPU driver is successfully initializing it.
lsmod | grep amdgpu
This command above shows whether the AMDGPU kernel module is loaded and what other kernel modules depend on it.
amdgpu 21188608 26
This tells us:
amdgpu is loaded
the module occupies roughly 21 MB of kernel module memory
26 references/users currently depend on it
dmesg | grep -i amdgpu | tail -50
The command above shows the last 50 kernel messages associated with AMDGPU initialization.
The most important things we see are:
MEM ECC is active.
The GPU’s VRAM ECC capability is active.
VRAM: 32624M
The kernel detects approximately 32 GB of VRAM.
Detected VRAM RAM=32624M, BAR=32768M
Preparing for ROCm Install
First lets make sure that the kernel has created the ROCm compute device
ls -l /dev/kfd
crw-rw---- 1 root render 235, 0 Aug 9 16:20 /dev/kfd
/dev/kfd is the Kernel Fusion Driver device that ROCm/HIP uses to submit compute workloads to the AMD GPU. AMD’s ROCm installation process relies on access through the render/video device groups.
In the output above, you can see that the Kernel Fusion Driver is owned by root with group membership of render. We need to add my user to the render group. Note that I had to reboot to pick up the changes.
sudo usermod -aG render cpaquin
Now we can add the ROCm 7.2.2 package repo, which is two steps. This one below…
gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null
And this next one to actually add the repo.
sudo tee /etc/apt/sources.list.d/rocm.list <<'EOF'
deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2.2 noble main
deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/graphics/7.2.1/ubuntu noble main
EOF
Now let’s be smart and ensure that the repo was added properly.
cat /etc/apt/sources.list.d/rocm.list
deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2.2 noble main
deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/graphics/7.2.1/ubuntu noble main
Now before we install anything, per AMD recommendation, we need to pin the above repo priority. Pinning means settings which repository Ubuntu prefers when the same package is available from multiple repositories, which means prefer the AMD repos over Ubuntu repos for AMD bits and bobbles.
sudo tee /etc/apt/preferences.d/rocm-pin-600 <<'EOF'
VRAM usage is 661 MB / 32,624 MB, which is normal for the display/driver stack.
No uncorrectable ECC errors: UEC 0.
No compute processes are currently using the GPU.
Test ROCm inside Docker
Can a Docker container access the R9700 through ROCm? Lets find out.
docker run --rm \
--device=/dev/kfd \
--device=/dev/dri \
--group-add 992 \
--group-add 44 \
rocm/rocm-terminal:latest \
rocminfo
What this does:
starts a temporary AMD ROCm container
passes /dev/kfd into it for ROCm compute
passes /dev/dri into it for the GPU device nodes
gives the container the needed video (44) and render (992) group access
runs rocminfoinside the container
deletes the container afterward because of --rm
If it works, we should see the same R9700 identification inside Docker that we saw on the host, including something like:
Pytorch Test
Next we’ll use AMD’s validated PyTorch container for ROCm 7.2 / Ubuntu 24.04 / Python 3.12 / PyTorch 2.9.1. AMD recommends Docker for PyTorch because the image already contains a tested PyTorch+ROCm combination.
This will likely download a fairly large container image the first time.
What we want to see is essentially:
PyTorch: 2.9.1+rocm7.2.0.git7e1940d4
ROCm: 7.2.26015-fc0010cf6a
GPU available: True
GPU: AMD Radeon AI PRO R9700
One potentially confusing point: PyTorch keeps the API name torch.cuda even when using AMD ROCm.
torch.cuda.is_available() returning True means PyTorch has a usable GPU backend; on this container that backend is HIP/ROCm, not NVIDIA CUDA. AMD uses this same test in its ROCm PyTorch verification instructions.
So the R9700 is now validated for actual ROCm/PyTorch compute inside Docker. Next Step VLLM and test some models.
One of my workstations has a Intel Core i7-8086K Special Edition CPU. Its a 40th-anniversary limited edition 6-core/12-thread Coffee-Lake processor launched in 2018 to commemorate the original Intel 8086 processor. The i7-8086K features a base clock speed of 4.0GHz and a boost speed of 5.0GHz.
As this processor was not a standard mass-produced model, its a bit collectible in some strange circles. It was also the first Intel processor to hit 5.0GHz out of the box. Technically the i7-8086K is a “cherry-picked” or higher-binned version of the popular Intel Core i7-8700K. Because these chips were selected from the best-performing parts of the wafer, they typically require less voltage to run at high speeds. At the time enthusiasts were willing to pay a premium as these chips are much more likely to reach stable overclocks of 5.1 GHz to 5.3 GHz across all cores.
Nowadays the Intel Core i7-8086K Special Edition CPU still maintains a “legacy premium” price, even on eBay where they are still over $100 per.
So lets overclock it a bit.
Installing CPU Frequency Tools in Ubuntu
Lets start by installing the tools to allow us to modify the cpu governor and inspect CPU frequencies
First we will run a quick frequency check. Here the CPU(s) scaling MHz indicates the percentage of the CPU’s maximum possible frequency that the system is currently running at (on average across cores). Meaning that the CPU is currently operating at ~42% of its maximum clock frequency across all CPUs.
lscpu | grep MHz
CPU(s) scaling MHz: 42%
CPU max MHz: 5100.0000
CPU min MHz: 800.0000
Via the cpupower command we can we that we currently have one boosted core running at 5.07Ghz.
root@raptor:/etc/chrony# cpupower frequency-info
analyzing CPU 7:
driver: intel_pstate
CPUs which run at the same hardware frequency: 7
CPUs which need to have their frequency coordinated by software: 7
energy performance preference: performance
hardware limits: 800 MHz - 5.10 GHz
available cpufreq governors: performance powersave
current policy: frequency should be within 800 MHz and 5.10 GHz.
The governor "performance" may decide which speed to use
within this range.
current CPU frequency: 5.07 GHz (asserted by call to kernel)
boost state support:
Supported: yes
Active: yes
We can run the watch command against /proc/cpu info and watch frequencies change real-time.
Generate Load to Drive Up Frequencies
In a second ssh window we will now install and run stress-ng to generate some load, while we continue to run our watch command above.
sudo apt install stress-ng
Once installed we run it as shown below
Where:
–cpu 0 = use all online CPUs
matrixprod is a decent compute-heavy load
In our watch window we can see we have all cores above 4.0GHz
Checking CPU Temps
My Intel Core i7-8086K Special Edition CPU has a pretty average cpu cooler/fan. Nothing fancy with heat-pipes and multiple fans, however I do have 8x 120mm case fans moving a lot of air through my case, while projecting nice purple glow. So lets check our CPU temps, first at rest and then under load.
So we start by installing lm-sensors, and then detect available sensors. Sensors-detect will ask you a handful of questions, but you can usually stick with the defaults.
sudo apt install lm-sensors
sudo sensors-detect
And now we check temps at rest
~# sensors
coretemp-isa-0000
Adapter: ISA adapter
Package id 0: +34.0°C (high = +82.0°C, crit = +100.0°C)
Core 0: +33.0°C (high = +82.0°C, crit = +100.0°C)
Core 1: +33.0°C (high = +82.0°C, crit = +100.0°C)
Core 2: +34.0°C (high = +82.0°C, crit = +100.0°C)
Core 3: +36.0°C (high = +82.0°C, crit = +100.0°C)
Core 4: +33.0°C (high = +82.0°C, crit = +100.0°C)
Core 5: +33.0°C (high = +82.0°C, crit = +100.0°C)
So lets kick off stress-ng to generate some load, which we ‘watch’ the output of the sensors command in another terminal window.
And we can see that we get a bit toasty. While these temperatures are high, they are not dangerous to see during a stress test. What we do know is that we do not have a lot of thermal headroom.
Stress-ng is built to generate worst-case thermal load when running, so I really and not concerned to see these temps when its running.
Just to be sure, lets run turbostat and see if we see any throttling.
sudo turbostat --interval 1
Turbostat produces a lot of output, the table below will give you an idea of what you are looking at.
Column
What it Shows
What You Want to See
PkgTmp
CPU package temperature
Ideally <90 °C during sustained load
CoreTmp
Per-core temperature
Similar to package temp; watch for cores hitting 95 °C+
Avg_MHz
Average CPU frequency during the interval
Should rise close to the overclock max under load
Bzy_MHz
Actual frequency while the core is busy
Should approach the configured turbo (≈5 GHz)
Busy%
CPU utilization per core
Near 100% during stress-ng
PkgWatt
Total CPU package power
Useful to understand thermal load
CorWatt
Power used by cores
Indicates how much compute load is occurring
For thermal events or power throttling we look for the following.
Column
Meaning
ThermThrot
Thermal throttling events
Pkg%pc2 / pc3 / pc6
Package idle states (should drop during stress)
Core%pc3 / pc6
Core idle states
If cpu performance/speed was throttled we would expect to see ThermThrot events.
If you want a cleaner, and more succinct output from turbostat, try the one-liner below.
While we are not seeing any throttling events, we do see that across all CPU cores we have a BZY_MHz of 4400 MHz (not 5000MHz). Since our test workload is running across all cores/threads (12 logical CPUs in this case), the processor switches the the all-core turbo limit of 4.4GHz which is lower than the single core turbo frequency of ~5.0GHz. This is good news and shows we are stable across all cores, but does not allow us to hit the max turbo frequency for this cpu.
So lets try that.
Lets run a load on exactly one core.
stress-ng --cpu 1 --taskset 0 --timeout 60
In another terminal window we run our watch command, where we can see that we are in fact able to hit almost 5.1Ghz.
Not that I am looking to push it any further above 5.0GHz, but if I was, a better cpu cooler would be the way to go (and possibly some tweaking in the bios). That being said, at our current speeds as we never got hot enough to see any throttling. May look for a glowing purple one anyway.
In this post I will cover setting up a ntp/chrony server on Ubuntu 22.04 using a Satellite GPS Receiver. I have also recently published another post on Chrony, which goes a bit more into basic commands. That post can be found here.
Note: the GlobalSat BU-353-W11 does not support 1pps. It’s designed primarily for positioning and navigation, not precision timing. We can use it as our timesource for chrony, with a typical accuracy: ±50–150 ms. Good enough for a lab.
Hardware Detection
After plugging in the USB cable on the GNSS module, check for a new USB device.
# lsusb
Bus 001 Device 004: ID 1546:01a7 U-Blox AG [u-blox 7]
Output from dmegs also shows the device was detected properly
[347055.572498] usb 1-2.4: new full-speed USB device number 4 using tegra-xusb
[347055.768706] cdc_acm 1-2.4:1.0: ttyACM0: USB ACM device
[347055.768784] usbcore: registered new interface driver cdc_acm
[347055.768788] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
In the output above you can see the device was detected ttyACM0. Now we need to confirm how the device was enumerated. As you can see the device is owned by root and the group is dialout
# ls -l /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0 Dec 29 13:40 /dev/ttyACM0
You can run a quick check to confirm that the device is functioning as shown below
To use a GPS/GNSS antenna with chronyd on Linux for time synchronization and location sharing, we will use the gpsd service to manage the GPS hardware and make its data accessible to other applications, including chronyd
Identify the GPS device: The device will likely be /dev/ttyUSB0, /dev/ttyACM0, or similar. You can verify this by running cat /dev/ttyUSB0 (replace ttyUSB0 with your suspected device) and looking for NMEA strings (lines starting with $GPGGA, $GNRMC, etc.).
Edit the gpsd configuration file: Open /etc/default/gpsd and set the DEVICES, GPSD_OPTIONS, and USBAUTO variables.
Fix:3D DGPS FIX – means the receiver has solved all three spatial dimensions: (Latitude, Longitude, Altitude)
Time is valid
Position accuracy: ~5–10 feet horizontal
Satellite Section (Right Pane)
Seen 13 / Used 11
This means:
The receiver can currently see 13 satellites
11 of those are strong enough to be used in the solution
This is quite a healthy signal for a device sitting inside up against a window.
Anything above:
4 used → valid 3D fix
8+ used → very solid geometry
10–12 used → excellent (we are here)
Constellations
GP = GPS
U.S. GPS constellation
Medium Earth Orbit (≈20,200 km)
~30 active satellites worldwide
SB = SBAS (WAAS corrections)
Satellite-Based Augmentation System
Broadcast correction data
Improve accuracy of GPS measurements
Do not provide independent position fixes
The presence of SBAS satellites indicates differential reminder data is available, improving accuracy.
Fix & Timing (Left Pane)
Fix State
Status: 3D DGPS FIX (1 secs)
3D = Latitude, longitude, altitude solved
DGPS = WAAS corrections applied
(1 secs) = Fix age (very fresh)
Time Quality
Time: 2025-12-29T20:32:22.000Z
Time offset: 0.078609772 s
Chrony
Install Chrony via apt
# apt install chrony -y
Configure Chrony
Edit /etc/chrony/chrony.conf. This config has been tested on Ubuntu 22.04, YMMV on other Linux versions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
After a few minutes, we should start to see pre-configured clients appear in the output of the command below
# chronyc clients
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
=============================================================================
scar.lab 5 0 6 - 30 0 0 - -
Chrony Client-side Setup
Ubuntu Client
$ sudo apt install chrony -y
Configure the to listen to the chrony server address 10.1.10.10. Comment out any other server or pool directives in /etc/chrony/chrony.conf.
server 10.1.10.11
Start and enable the service.
$ sudo systemctl enable chrony --now
Check configured time source for chrony
$ chronyc -n sources -v
What the above output tells you
All NTP servers/pools configured for this client
Which source is currently selected
Reachability and time quality
Key indicators
Symbol
Meaning
^*
Current sync source
^+
Candidate source
^-
Reachable, not selected
^?
Unusable / not trusted yet
Cisco Catalyst Client Config
Use the commands below to configure your Cisco switch to use the new timesource
# Enter global configuration mode
configure terminal
# Define the NTP server
ntp server 10.1.10.11
# (Optional) Set the switch to use its own hardware clock if it loses sync
ntp master 10
# Exit and save
end
write memory
We are installing on a very fresh install of Ubuntu 22.04 with an Nvidia 3070ti GPU. Nvidia Drivers were installed at build time. However lets check to see what version of the Nvidia driver we are currently running.
$ nvidia-detector
nvidia-driver-570
While we are at it, let’s make sure that we have nvidia-smi installed which is included in the package shown below
$ sudo apt install nvidia-utils-570
We will need gcc, which the meta-package below will include (along with make and other dev tools).
# sudo apt install build-essential -y
Lets check what version of python we have installed.
$ python3 --version
Python 3.10.12
Now install python-venv which is the python module that supports creating virtual environments. Reference the python version shown in the output in the step above
# sudo apt install python3.10-venv -y
Installing the Nvidia Cuda Toolkit
Note: If you installed the nvidia-cuda-toolkit from default ubuntu noble repo, uninstall it first as that version is probably too old. Uninstall with “sudo apt purge nvidia-cuda* -y“
“The NVIDIA CUDA Toolkit is a software development kit that helps users create GPU-accelerated applications. It includes libraries, compilers, debuggers, and optimization tools”. Since we have an Nvidia GPU we will install it as shown below.
First we wget the Nvidia Cuda Repository pin as shown above. Then we move it to /etc/apt/preferences.d/ (shown below).
This is a configuration file that is used to prioritize packages from the NVIDIA CUDA repository when installing CUDA on a Linux system. Basically it tells apt where to get the Cuda Toolkit.
Nvidia states that you need to add the following env vars to your .bashrc, however you will also need to add them to your python virtual env — in venv/bin/activate. These variables will be needed during the install process below.
Note that every time you want to use instructlab cli (ilab) post-install you must source the file above. venv/bin/activate
The install guide found here has you run this command to clear out the pip package cache (for this one package. Not required for first install, but may be on subsequent install runs.
$ pip cache remove llama_cpp_python
We then run the 3 commands shown below. Apparently there is a known bug and we will not pick up these required packages as needed, so we need to install them manually, and in the order shown below.
The instructions have you wrap up the of Instructlab with a pip install of vllm, however this install will fail due as it cannot find nvcc without setting the env variables. So set the following env vars as shown below.
The instructions for installing instructlab with Nvidia Cuda support as documented on instructlab.ai currently have you pip install a very specific version of vllm (with cuda support), however I have found that this command will fail (at least on Fedora 40, Ubuntu 22.04, and Ubuntu 24.04) that command is shown below.
One up and running you will see output similar to what is shown below.
INFO: Started server process [1252947]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
Testing Pytorch Module
You can create and run the python script below to test if your GPU is identified correctly. This step is not required, but its handy in identifying potential issues with gpu/cuda.
$ python3 ./test.py
CUDA available: True
CUDA version: 12.4
Torch version: 2.5.1+cu124
GPU count: 1
GPU name: NVIDIA GeForce RTX 3070 Ti
Instructlab Init and Setup
Now that we have ilab installed we can initialize our environment with the command below. This command will download the Taxonomy repository to our local machine along with a config file. Apparently ilab init has a few default GPUs available to choose from, but since my current GPU is
Our config file is located in our home directory, if you want to take a look. Note that our GPU, being a Nvidia RTX 3070 TI, will have differing config values than what we selected above (A100), so in a later step we will need to edit our config
/home/cpaquin/.config/instructlab/config.yaml
To see the info regarding your workstation, you can run the command below. We will use some of the data below to update our config
Use the command below to edit the generated config.
$ ilab config edit
Here we go through the file and ensure that we are setting the number of GPUs correctly. We also modify some additonal settings that have differed from the default (as ilab thinks we now have a $40k GPU). Below is some of what we have configured.
# Metadata pertaining to the specifics of the system which the Configuration is
# meant to be applied to.
metadata:
# Manufacturer, Family, and SKU of the system CPU, ex: Apple M3 Max
# Default: None
cpu_info:
# Amount of GPUs on the system, ex: 8
# Default: None
gpu_count: 1
# Family of the system GPU, ex: H100
# Default: None
gpu_family: RTX 3070 TI
# Manufacturer of the system GPU, ex: Nvidia
# Default: None
gpu_manufacturer: Nvidia
# Specific SKU related information about the given GPU, ex: PCIe, NVL
# Default: None
gpu_sku:
Working with Models
Before we start working with a model, lets take a look at the ilab command parameters. We can see that we have several options such as download, list, test, train, chat, serve, etc.
Lets download a model with the command below. The command below will download the default model sets in the config file
$ ilab model download
output below
View your downloaded models via the command below
$ ilab model list
Now we serve the default model with the command below
$ ilab model serve
Open another terminal window and source the env file from the instructlab directory
$ source venv/bin/activate
Then run the command below to begin an interactive chat session
This 2 part guide is written specifically for those who are just getting started with Ollama. Note that I originally wrote this post with the Nvidia Jetson Orin Nano in mind, as that is where I was initially running Olama… in part 2 I switch to something more powerful.
That being said, the information below regarding installing Ollama and pulling a model is not specific to the Jetson and should work for anyone who wants to get started quickly with Ollama. Also worth noting that in this quick start guide we are installing ollama as a service, not as a container, as you would do if using jetson-containers [1]
Installing ollama
Use the command below to install ollama.
$ sudo curl -fsSL https://ollama.com/install.sh | sh
The install script downloads ollama, required Jetpack 6 components (on Jetson Devices), creates the ollama user, creates an api endpoint, and enables & starts the ollama service.
$ sudo curl -fsSL https://ollama.com/install.sh | sh
>>> Installing ollama to /usr/local
>>> Downloading Linux arm64 bundle
######################################################################## 100.0%
>>> Downloading JetPack 6 components
######################################################################## 100.0%
>>> Creating ollama user...
>>> Adding ollama user to render group...
>>> Adding ollama user to video group...
>>> Adding current user to ollama group...
>>> Creating ollama systemd service...
>>> Enabling and starting ollama service...
Created symlink /etc/systemd/system/default.target.wants/ollama.service → /etc/systemd/system/ollama.service.
>>> NVIDIA JetPack ready.
>>> The Ollama API is now available at 127.0.0.1:11434.
>>> Install complete. Run "ollama" from the command line.
Run the command below to test functionality and verify installed version of ollama.
$ ollama --version
ollama version is 0.5.7
Configure ollama to Listen on all Interfaces
By default ollama listens on 127.0.0.1. If you want to configure it to listen on all interfaces so that you can interact with it remotely, you will need to modify the service configuration as shown below.
Test connectivity from a remote host using telnet.
$ telnet 10.1.10.11 11434
Configure Alternative Download Directory For Ollama Models
Obviously this step is optional, but you can set the download directory if wanted with the parameter below. Apparently the default location is “~/.ollama/models”
Let’s config successful download and install with “ollama list“
$ ollama list
NAME ID SIZE MODIFIED
llama2:7b 78e26419b446 3.8 GB 3 minutes ago
Interacting with Ollama via the CLI
You can interact with Ollama (and the loaded model) via the CLI in one of two ways.
Predefined Prompt
Interactive Mode
Via Predefined Prompt
In this mode, you call the model and pass the prompt in one step
~$ ollama run llama2:7b "Tell me about the Nvidia Jetson Orin Nano"
Interactive Mode
ollama run llama2:7b
A Wild Error Appears
While attempting to run an interactive session, we see that the ollma runner process was terminated
~$ ollama run llama2:7b
Error: llama runner process has terminated: signal: killed
Let’s watch the output of journalctl and watch for errors as we try again
sudo journalctl -f -u ollama.service
While watching journalctl we see the following
Feb 02 17:21:26 jetson.lab ollama[2306]: time=2025-02-02T17:21:25.650-05:00 level=WARN source=server.go:562 msg="client connection closed before server finished loading, aborting load"
Feb 02 17:21:26 jetson.lab ollama[2306]: time=2025-02-02T17:21:25.668-05:00 level=ERROR source=sched.go:455 msg="error loading llama server" error="timed out waiting for llama runner to start: context canceled"
Feb 02 17:21:26 jetson.lab ollama[2306]: [GIN] 2025/02/02 - 17:21:25 | 499 | 4.679059726s | 127.0.0.1 | POST "/api/generate"
Feb 02 17:21:26 jetson.lab systemd[1]: ollama.service: Failed with result 'oom-kill'.
Apparently we are getting “oom-killed“.
Lets watch free memory while we execute “ollama run” again. We will “watch” free -m
watch -d -n 1 free -m
We can see free memory drop to about 500mb, which may or may not be enough to run the rest of the system. When ollama is not running we have about 2485MB of free memory (shown below)
$ free -m
total used free shared buff/cache available
Mem: 3601 940 2485 0 176 2483
Swap: 1800 338 1462
So we have a few options
Attempt to reduce the amount of free memory available to Ollama (lets try)
Tune ollama to attempt to use less memory (might be possible)
Modify oom-killer behavior (probably a bad idea)
Change to a lighter weight model (best idea)
Freeing up System Memory
Lets disable the Desktop GUI in ubuntu and see what that buys us in free memory. Note that you can probably just skip this section as I eventually move to a smaller model, but there is some good troubleshooting information here for those new to Linux.
# sudo systemctl set-default multi-user.target
And now immediately move to cli mode without reboot
$ sudo init 3
Output from ‘free -m” has not changed.
$ sudo free -m
total used free shared buff/cache available
Mem: 3601 859 2402 4 339 2554
Swap: 1800 164 1636
We will reboot just in case…. ok that is a tiny bit better
$ sudo free -m
total used free shared buff/cache available
Mem: 3601 458 2709 18 433 2938
Swap: 1800 0 1800
Now lets see if we can find a few services that we do not need, and stop and disable them.
Then reboot. Once back up and running a cursory check of “free -m” shows that our efforts were mostly in vain. Lets try another model
So this time we are going with a small model to test basic functionality and see if we are still running into oom errors.
ollama pull tinyllama
Ok much better…
~$ ollama run tinyllama
>>> Send a message (/? for help)
Lets check memory, and see how much headroom we have… Its not a lot
~$ free -m
total used free shared buff/cache available
Mem: 3601 1835 924 0 841 1575
Swap: 1800 147 1653
Inspecting System Utilization
For this step we are going to use a couple of tools, most of which are custom to the Jetson.
First we launch jtop [2]. And n another terminal windows, we load up the tinylama model and enter our prompt. While ollama is working, we watch observe jtop.
So our prompt goes…
can you tell me the history of the company Digital and their line of PDP computers
Certainly! The company Digital was founded in England in 1963 by two college students, John Cocking and Michael Kearns. They were inspired to start a computer company after
witnessing the emergence of the personal digital assistant (PDA) market, which had been dominated by smaller, less advanced companies such as Acorn Computers and Marmalade.....trunc...
While this is running we are watching jtop. Below we can see that our GPU is at 80% load and at times approached 100%. Memory usage is high, and CPU utilization is low.
Measuring Tokens Per Second
This seems to be the measurement that many use to determine how fast their machine is, so lets give it a try. To output tokens per second we add the “–verbose” flag.
~$ ollama run tinyllama --verbose "Can you tell me as much as you know about the Dell T620 Server"
I get a 6 bullet list that is almost completely incorrect. What exactly is a DelT620? And no the T620 was intel xeon powered, not powered by AMD’s EPYC 7551 processor. But that does not matter, what comes next is what we are looking for.
total duration: refers to the total time taken for the entire process of generating a response. This includes all stages such as: – Model Loading: If model isn’t already loaded into memory, this ime accounts for loading it. – Tokenization: Converting input text into tokens that the model can process. – Inference Time: The time spent by the model generating the response token by token. – Post-processing: Any steps taken after generation, such as formatting the output. – Communication Overhead: Time spent handling requests and responses, especially if running in a client-server setup.
load duration
1.246583429s
load duration: refers to the amount of time spent loading the model into memory before it can start processing input. This step includes: – Model Retrieval: If the model is not already cached in memory, Ollama retrieves it from disk or another source. – Model Initialization: Preparing the model, including loading weights into VRAM (if using a GPU) or RAM (if running on a CPU). – Graph Compilation (if applicable): Some backends may optimize or compile the model for execution. – Memory Allocation: Ensuring that enough memory is available for inference.
prompt eval count
52 token(s)
prompt eval count: refers to the number of tokens that were processed (evaluated) from the initial prompt before the model starts generating a response. Breakdown: – Prompt Tokens: Before the model generates any output, it first processes (evaluates) the input text (prompt). – Evaluation: The model tokenizes the input text and processes these tokens through its neural network. – Count Meaning: This number represents how many tokens were in the prompt that the model had to evaluate before responding.
prompt eval duration
283ms
“prompt eval duration” refers to the total time taken to process (evaluate) the input prompt before the model begins generating a response. Breakdown: – Tokenization: The input text is broken down into tokens that the model can process. – Model Inference on Prompt Tokens: The model evaluates these tokens, passing them through its neural network layers to set up the internal context. – Context Initialization: The model updates its internal state based on the prompt before starting to generate output.
prompt eval rate
183.75 tokens/s
prompt eval rate” refers to the speed at which the model processes the tokens from the input prompt. Measured in tokens per second (tokens/sec) where prompt-eval-rate = prompt eval count/prompt eval duration
eval count
464 tokens/s
“eval count” refers to the total number of tokens that were processed during the generation phase—i.e., the number of new tokens the model generated as output.
eval duration
24.354s
“eval duration” refers to the total time taken for the model to generate its response, measured from the moment it starts producing tokens until it finishes.
eval rate
19.05 tokens/s
“eval rate” refers to the speed at which the model generates tokens during the response phase. It is typically measured in tokens per second (tokens/sec). – eval_count / eval_duration = eval rate
Customizing Model Behavior
You can customize how the model responds with the set command. For example, lets say I want to tell the model to respond to me as if it were a child
$ ollama run tinyllama
>>> /set system For any question asked can you respond as if you were a child, using only small words, short sentences, and lacking any specific technical details or jargon
Set system message.
>>> /save kid2
Created new model 'kid2'
>>> /bye
Then we load the replicated model.
~$ ollama run kid2
>> what was the US civil war about
The United States Civil War was fought between the Union and Confederate states over whether the union should remain intact as a single nation or become a separate nation known
as the Confederate States of America. The war began in April 1861 after President Abraham Lincoln announced that he would veto any bill to abolish the constitutional right to
secession unless Congress passed a similar law.
As the war progressed, each side unleashed their forces on the other and suffered significant losses. The north won major victories in the first year of the war, but southern
states began withdrawing from the union due to economic hardship, making it necessary for Lincoln to appeal to them with a proposal to call off fighting and establish a
government in the south. In response, the Confederate army launched its own attack on April 12, 1865, which led to Lincoln's assassination a few months later. The US civil war
was one of the most devastating conflicts in world history, causing significant destruction to both sides and leaving millions dead or wounded.
Ok, so that was not exactly child-like, however this is probably the fault of the model that I am using. Lets see if I can coax it to answer the question differently.
>> /set system For any question asked can you respond as if you were a scholar, using a large vocabulary of diverse words. Be very specific in your responses and provide colorful deta
... ils
Set system message.
>>> what was the US civil war about
The United States Civil War was fought between the Union and Confederate states over whether the union should remain intact as a single nation or become a separate nation known
as the Confederate States of America. The war began in April 1861 after President Abraham Lincoln announced that he would veto any bill to abolish the constitutional right to
secession unless Congress passed a similar law.
The main cause of the war was the belief by some members of the southern states that they had been wronged and mistreated by the federal government, as well as a desire for
self-determination and statehood. The north won major victories in the first year of the war, but southern states began withdrawing from the union due to economic hardship,
making it necessary for Lincoln to appeal to them with a proposal to call off fighting and establish a government in the south. In response, the Confederate army launched its own
attack on April 12, 1865, which led to Lincoln's assassination a few months later. The US civil war was one of the most devastating conflicts in world history, causing
significant destruction to both sides and leaving millions dead or wounded.
Ok so not great, but again probably the model. Anyway you get the point. I’ll try this again with a different model in the near future of one of my more powerful servers/
Removing a Model
Remove a model with the “rm” switch. See below
~$ ollama list
NAME ID SIZE MODIFIED
kid2:latest ca8452f00cd5 637 MB 2 hours ago
kid:latest ce8e59f0d306 637 MB 2 hours ago
tinyllama:latest 2644915ede35 637 MB 3 hours ago
llama2:7b 78e26419b446 3.8 GB 5 hours ago
cpaquin@jetson:~$ ollama rm kid:latest kid2:latest llama2:7b
deleted 'kid:latest'
deleted 'kid2:latest'
deleted 'llama2:7b'
Training a Model
Most available models operate pretty well when you are asking them about information that they have been trained on. Above you can see that the tinyllama model has not been trained properly to even remotely respond as if it was a child. In order to do that, the model would need to be trained.
So lets train tinyllama. Keep in mind that these models have short-term memory constraints, meaning they only retain training data during an active conversation. Once you close the session and start a new one, the model will not remember any information from the previous interaction. To overcome the short-term memory limitation, one would need a backend database or some form of persistent storage, which would you to save and retrieve relevant information across sessions. Depending on your needs, you could use:
Relational Databases (SQL) – MySQL, PostgreSQL, or SQLite for structured data.
NoSQL Databases – MongoDB, Redis, or Firebase for more flexible storage.
Vector Databases – Pinecone, FAISS, or ChromaDB for storing embeddings in AI applications.
File Storage – JSON, CSV, or other formats for lightweight persistence.
The backend system could then integrate with the AI model, fetching and updating information as needed, effectively giving it “memory” beyond a single session. More about this later. For now let’s “teach” tiny llama about Star Trek. Specifically, I prompted the model with this question, and its response was incomplete.
>>> do you know about any of the star trek tv shows
Certainly! Here are some popular Star Trek TV shows:
1. Star Trek: The Original Series (1966-1968)
2. Star Trek: The Next Generation (1987-1994)
3. Star Trek: Deep Space Nine (1993-1999)
4. Star Trek: Voyager (1995-2001)
5. Star Trek: Enterprise (2001-2005)
6. Star Trek: Discovery (2017-)
I then made sure that was all the data it had on Star Trek TV shows.
>>> is that all the star trek shows
Yes, that's all the Star Trek TV series I could find.
And we are off to the races! Here is what I “taught” the model
Let me tell you about some other Start trek TV shows that you are unaware of. Star Trek: The Animated Series premiered in 1972 and ran for 2 seasons, it featured the same character
... s that appeared Star Trek: The Original Series. Star Trek: Lower Decks premiered in 2020 and ran for 5 seasons. Sadly it was recently cancelled. It was an animated show which was
... comedic in nature, often making fun of the tropes features throughout the other Star Trek shows and movies. Star Trek: Prodigy premiered in 2021 and ran for 2 seasons, it was an an
... animated show that originally aired on the kids station Nickelodeon. It was geared towards children, but was still fun for adults to watch as well. Especially with their children.
... Star Trek: Strange New Worlds premiered in 2022 and has been running for 2 seasons, a third season is on its way. This show takes place on the starship Enterprise, but before Capta
... in Kirk was in command. The Captain, is Captain Pike, who was a character originally introduced in the 2 part pilot episode of the Original Series. Would you like to know more about star trek?
Here we are witnessing what is called a hallucination which is when an artificial intelligence (AI) model generates an incorrect or misleading output.
We do see that the model does now know about Star Trek: Strange New Worlds, but it basically made up the plot. There is no Captain Alex Danforth anywhere on google, and I have never heard of the U.S.S Explorer. Ava Acres is a real actor, but has nothing to do with Star Trek. James Frain played Sarek in Star Trek Discovery, so at least he is associated with the series. The model did remember that I mentioned a Star Trek show that aired on Nickelodeon, and got the series dates correct, but associated it with a completely made up show.
Fascinating…
>>> Tell me about Star Trek: Strange New Worlds
Yes, I do have more information on Star Trek: Stragrg New Worlds. This is a science fiction TV show that originally aired on Nickelodeon between 2021 and 2023. It's set in the
same universe as Star Trek: Lower Decks, but takes place in the 25th century.
The show follows the crew of the U.S.S. Explorer, a new ship that was built specifically for this mission. The crew is made up of Captain Alex Danforth (played by James Frain),
First Officer Renee "Ren" Dahla (played by Ava Acres), and Engineer Jesse Johnson (played by David Hewison).
The Explorer has been sent to explore a new planet that's rich in resources. The crew encounters various alien species, including a group of intelligent, hostile beings who have
been observing their activities for some time.
The show is filled with action and adventure as the Explorer faces numerous challenges on its mission. It also explores themes such as colonialism, power dynamics, and
responsibility to others. The show's premise may seem familiar to Star Trek fans, but it features new characters and a fresh take on the classic series universe.
If you enjoy Nickelodeon-style science fiction and have enjoyed Star Trek: Lower Decks, then I think you will enjoy Star Trek: Stragrg New Worlds.
So I have recently made the switch to Kubuntu 12.10 on my new desktop. Basically I am building a monster workstation and I ran into issue running Fedora 18. Since I wanted Steam support too I decided not to try to move to an earlier Fedora version, but rather, I chose to give Ubuntu another try (its been years since I have run Ubuntu).
Anyway, I am building this hoss of a work station that has 12 Dimm slots, which I fully populated with 4GB dims. However when I booted my new monster, I found that I was 8GB short in the Memory department.
So, how do I figure out which two dimms are bad? I certainly dont want to have to pull all of them out and boot the machine and test each dimm one by one.
So this is where lshw comes to the rescue. Which I blogged about back in 2010 here.