Modern AI assistants like ChatGPT have fundamentally changed user expectations around conversational interfaces. Users now expect to have coherent, multi-turn conversations where the AI remembers what was said earlier in the discussion. However, when building AI-powered bots on top of messaging platforms like Signal, Telegram, or SMS, developers face a fundamental architectural challenge: these platforms are inherently stateless. Each message arrives as an independent event with no built-in mechanism for maintaining conversational context.
This paper examines a production implementation that bridges this gap, enabling persistent multi-turn AI conversations over Signal's stateless messaging protocol. We explore the database schema design, the command parsing architecture, and a novel inline image reference system that allows users to incorporate visual context into ongoing conversations.
1. Introduction
1.1 The Statefulness Problem
Large Language Models (LLMs) like GPT-4 and GPT-5 are stateless by design. Each API call is independent—the model has no memory of previous interactions unless the developer explicitly includes conversation history in each request. Services like ChatGPT create the illusion of memory by maintaining conversation state server-side and replaying the full message history with each new user input.
When building a bot on a messaging platform, developers must solve this same problem, but with additional constraints:
Message Independence: Each incoming message from Signal (or similar platforms) arrives as a discrete event with no connection to previous messages.
Multi-User Environments: In group chats, multiple users may be conducting separate conversations with the bot simultaneously.
Asynchronous Delivery: Messages may arrive out of order or with significant delays.
Platform Limitations: Most messaging APIs provide no native support for threading or conversation tracking.
Resource Constraints: Storing complete conversation histories for every interaction can become expensive, both in terms of storage and API costs (since longer histories mean more tokens per request).
1.2 Design Goals
Our implementation targets the following objectives:
Conversation Continuity: Users should be able to continue previous conversations by referencing a conversation ID.
New Conversation Simplicity: Starting a fresh conversation should require no special syntax—just send a message.
Multi-Modal Support: Users should be able to reference images stored in the system within their conversational context.
Cost Transparency: Each response should report the API cost and attribute it correctly for multi-user billing scenarios.
Thread Safety: The system must handle concurrent conversations from multiple users without data corruption.
2. Database Schema Design
2.1 Conversation Tables
The persistence layer uses SQLite with a straightforward two-table design:
The gpt_conversations table serves as a lightweight header, storing only the conversation ID and creation timestamp. The actual message content lives in gpt_conversation_messages, which maintains the full history of each conversation.
2.2 Schema Rationale
Several design decisions merit explanation:
Minimal Conversation Metadata: The gpt_conversations table intentionally stores minimal information. We considered adding fields like user_id, title, or summary, but found these complicated the implementation without providing sufficient value. The conversation ID alone is enough to retrieve and continue any conversation.
Text Storage for Timestamps: Rather than using SQLite's native datetime types, we store ISO 8601 formatted strings. This provides timezone awareness (critical for a system serving users across time zones) and human readability when debugging.
Content as Plain Text: The content field stores the raw message text, not a structured format. This keeps the schema simple and avoids premature optimization. When multi-modal content (like inline images) is needed, we resolve references at query time rather than storing binary data in the conversation history.
Foreign Key Constraints: The foreign key relationship between messages and conversations ensures referential integrity and enables cascading deletes if conversation cleanup is needed.
3. Conversation Management API
3.1 Core Operations
The database abstraction layer exposes three primary operations:
defcreate_gpt_conversation(first_message:GPTMessage)->int:"""Create a new conversation and return its ID."""withget_db_connection()asconn:cur=conn.cursor()cur.execute("INSERT INTO gpt_conversations (created_at) VALUES (?)",(pendulum.now("America/Chicago").isoformat(),),)new_id=cur.lastrowidconn.commit()add_message_to_conversation(new_id,first_message)returnnew_id
The create_gpt_conversation function atomically creates both the conversation record and its first message. This ensures that no conversation exists without at least one message, maintaining data consistency.
defadd_message_to_conversation(conversation_id:int,message:GPTMessage):"""Append a message to an existing conversation."""withget_db_connection()asconn:cur=conn.cursor()cur.execute("""INSERT INTO gpt_conversation_messages (conversation_id, created_at, role, content) VALUES (?, ?, ?, ?)""",(conversation_id,pendulum.now().isoformat(),message.role,message.content),)conn.commit()
defget_messages_for_conversation(conversation_id:int)->List[GPTMessage]:"""Retrieve all messages in chronological order."""withget_db_connection()asconn:cur=conn.cursor()cur.execute("""SELECT created_at, role, content FROM gpt_conversation_messages WHERE conversation_id = ? ORDER BY created_at ASC""",(conversation_id,),)rows=cur.fetchall()return[GPTMessage(role=row[1],content=row[2])forrowinrows]
3.2 The GPTMessage Data Class
Messages are represented using a simple data class that mirrors the OpenAI API's message format:
@dataclassclassGPTMessage:role:str# "user", "assistant", or "system"content:str# The message text (or structured content for multi-modal)
This alignment with the OpenAI API structure means messages can be retrieved from the database and passed directly to the API without transformation, reducing complexity and potential for bugs.
4. Command Parsing and Conversation Flow
4.1 Command Syntax
The bot supports an optional conversation ID in its command syntax:
This is implemented via a regex pattern that makes the conversation ID optional:
def_process_gpt_command(text:str,command:str,model:GPTModel)->bool:pat=rf"^{command} (\d+ )?\s?(.*)"m=re.search(pat,text,flags=re.IGNORECASE|re.DOTALL)ifnotm:returnFalseconversation_id=m.groups()[0]# None if not providedprompt=m.groups()[1]
4.2 Conversation Branching Logic
The command handler implements distinct paths for new versus continued conversations:
ifconversation_id:# Continue existing conversationsignal_archive_db.add_message_to_conversation(conversation_id,GPTMessage(role="user",content=prompt))messages=signal_archive_db.get_messages_for_conversation(conversation_id)conv_id=conversation_idelse:# Start new conversationfirst_message=GPTMessage(role="user",content=prompt)conv_id=signal_archive_db.create_gpt_conversation(first_message)messages=signal_archive_db.get_messages_for_conversation(conv_id)
For continued conversations, we first persist the new user message, then retrieve the complete history. For new conversations, we create the conversation record (which automatically adds the first message), then retrieve it back. This ensures consistency—what we send to the API exactly matches what's stored in the database.
4.3 Response Handling and Storage
After receiving the AI's response, we store it as an assistant message:
gpt_response=gpt_api.gpt_completion(api_messages,model=model)response_text=gpt_response.get("text","Error: No text in response")bot_message=GPTMessage(role="assistant",content=response_text)signal_archive_db.add_message_to_conversation(conv_id,bot_message)send_message(f"[conversation {conv_id}] {response_text}\n"f"cost: \${cost:.4f}, payer: {payer}")
The response always includes the conversation ID, making it easy for users to continue the conversation later. Including cost and payer information provides transparency in multi-user environments where API expenses are shared or attributed.
Signal allows sending images as attachments, but these are ephemeral—they arrive with the message and aren't easily referenced later. For AI conversations, users often want to ask follow-up questions about an image discussed earlier, or reference images from the bot's archive in new conversations.
5.2 The imageid= Syntax
We implemented a lightweight markup syntax that lets users embed image references in their prompts:
gpt imageid=123 What's happening in this image?
gpt 42 imageid=123 imageid=456 Compare these two images
The syntax is intentionally simple—imageid= followed by a numeric ID. Multiple images can be included in a single prompt.
5.3 Implementation
Image references are resolved at request time through a two-stage process:
IMAGE_ID_REGEX=re.compile(r"imageid=(\d+)",re.IGNORECASE)def_build_inline_image_content(prompt:str)->tuple[list|str,list[int]]:"""Convert imageid= references to OpenAI API image payloads."""image_ids=IMAGE_ID_REGEX.findall(prompt)ifnotimage_ids:returnprompt,[]contents:list[dict]=[]cleaned_prompt=IMAGE_ID_REGEX.sub("",prompt).strip()contents.append({"type":"text","text":cleaned_prompt})embedded_ids:list[int]=[]forraw_idinimage_ids:image_id=int(raw_id)image_result=image_manager.get_image_by_id(image_id)ifnotimage_result:raiseValueError(f"Image ID {image_id} not found")_,image_path=image_resultimage_bytes=image_manager.read_image_bytes(image_path)image_b64=base64.b64encode(image_bytes).decode("utf-8")contents.append({"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{image_b64}"}})embedded_ids.append(image_id)returncontents,embedded_ids
The function extracts image IDs from the prompt, removes the imageid= markers from the text, loads each referenced image from disk, base64-encodes it, and constructs the multi-modal content structure expected by the OpenAI API.
5.4 Applying to Full Conversations
Since conversations may span multiple messages with image references, we apply this transformation to the entire message history:
def_prepare_messages_with_inline_images(messages:list[GPTMessage],)->tuple[list[GPTMessage],list[int]]:"""Transform all messages, resolving image references."""prepared:list[GPTMessage]=[]referenced_image_ids:list[int]=[]formessageinmessages:content=message.contentifmessage.role=="user"andisinstance(content,str):content,ids=_build_inline_image_content(content)referenced_image_ids.extend(ids)prepared.append(GPTMessage(role=message.role,content=content))returnprepared,referenced_image_ids
This approach means the database stores the original imageid= references as plain text, while the actual image data is resolved fresh for each API call. This has several advantages:
Storage Efficiency: We don't duplicate image data in conversation history.
Image Updates: If an image is re-processed or corrected, subsequent conversation continuations automatically use the updated version.
Auditability: The stored conversation clearly shows which images were referenced.
6. Concurrency and Thread Safety
6.1 Threading Model
Each command runs in its own daemon thread to avoid blocking the main message processing loop:
The system tracks which user initiated each request for cost attribution. Since this context is stored in thread-local storage, we must capture it before spawning the worker thread and restore it inside the thread:
current_user_context=gpt_api.get_user_context()defmy_func():try:gpt_api.set_user_context(current_user_context)# ... API calls use this context for billing ...finally:gpt_api.clear_user_context()
6.3 Database Connection Safety
SQLite connections are managed via context managers, ensuring proper cleanup even if exceptions occur:
Each database operation acquires its own connection, avoiding issues with SQLite's threading limitations while maintaining data consistency.
7. Practical Considerations
7.1 Conversation Length and Token Limits
As conversations grow, they consume more tokens per API call. The current implementation sends the complete history with each request, which can become expensive for long conversations. Production deployments might consider:
Summarization: Periodically summarizing older messages to reduce token count.
Windowing: Only sending the N most recent messages.
Smart Truncation: Using the model to identify and retain the most relevant context.
7.2 Error Handling
The implementation includes robust error handling for common failure modes:
try:api_messages,embedded_images=_prepare_messages_with_inline_images(messages)exceptValueErrorase:logger.error(f"Failed to attach images for GPT request: {e}")send_message(str(e))return
Invalid image references fail fast with clear error messages rather than sending malformed requests to the API.
7.3 User Experience
The response format provides all information users need to continue conversations:
[conversation 42] Here's my analysis of the image...
cost: \$0.0234, payer: jon
Users can immediately reference conversation 42 in their next message to continue the discussion.
8. Conclusion
Building persistent conversational AI over stateless messaging platforms requires careful consideration of data modeling, state management, and user experience. Our implementation demonstrates that a relatively simple database schema combined with thoughtful command parsing can provide a seamless multi-turn conversation experience.
The inline image reference system shows how platform limitations can be overcome through creative syntax design, allowing users to build rich multi-modal conversations without the messaging platform's native support.
This architecture has proven robust in production, handling concurrent users, long-running conversations, and multi-modal content while maintaining data consistency and providing transparency into API costs. The patterns described here are applicable beyond Signal to any stateless messaging platform where persistent AI conversations are desired.
Building a Nightly AI Code Scanner with vLLM, ROCm, and JIRA Integration
I've been running a ballistics calculation engine — a Rust physics library with several components, like a Flask app wrapper with machine learning capabilities, bindings for a python library as well as a Ruby gem library. There are also Android and iOS apps, too. The codebase has grown to about 15,000 lines of Rust and another 10,000 lines of Python. At this scale, bugs hide in edge cases: division by zero, floating-point precision issues in transonic drag calculations, unwrap() panics on unexpected input.
What if I could run an AI code reviewer every night while I sleep? Not a cloud API with per-token billing that could run up a $500 bill scanning 50 files, but a local model running on my own hardware, grinding through the codebase and filing JIRA tickets for anything suspicious.
This is the story of building that system.
The Hardware: AMD Strix Halo on ROCm 7.0
I'm running this on a server with an AMD Radeon 8060S (Strix Halo APU) — specifically the gfx1151 architecture. This isn't a data center GPU. It's essentially an integrated GPU with 128GB of shared memory; configured to give 96GB to VRAM and the rest to system RAM. Not the 80GB of HBM3 you'd get on an H100, but enough to run a 32B parameter model comfortably.
The key insight: for batch processing where latency doesn't matter, you don't need bleeding-edge hardware. A nightly scan can take hours. I'm not serving production traffic; I'm analyzing code files one at a time with a 30-second cooldown between requests. The APU handles this fine.
The HSA_OVERRIDE_GFX_VERSION environment variable is critical. Without it, ROCm doesn't recognize the Strix Halo architecture. This is the kind of sharp edge you hit running ML on AMD consumer hardware.
Model Selection: Qwen2.5-Coder-7B-Instruct
I tested several models:
Model
Parameters
Context
Quality
Notes
DeepSeek-Coder-V2-Lite
16B
32k
Good
Requires flash_attn (ROCm issues)
Qwen3-Coder-30B
30B
32k
Excellent
Too slow on APU
Qwen2.5-Coder-7B-Instruct
7B
16k
Good
Sweet spot
TinyLlama-1.1B
1.1B
4k
Poor
Too small for code review
Qwen2.5-Coder-7B-Instruct hits the sweet spot. It understands Rust and Python well enough to spot real issues, runs fast enough to process 50 files per night, and doesn't require flash attention (which has ROCm compatibility issues on consumer hardware).
vLLM Setup
vLLM provides an OpenAI-compatible API server that makes integration trivial. Here's the startup command:
The --max-model-len 16384 limits context to 16k tokens. My code files rarely exceed 500 lines (truncated), so this is plenty. The --gpu-memory-utilization 0.85 leaves headroom for the system.
I run this in a Python venv rather than Docker because ROCm device passthrough with Docker on Strix Halo is finicky. Sometimes you have to choose pragmatism over elegance.
Docker Configuration (When It Works)
For reference, here's the Docker Compose configuration I initially built. It works on dedicated AMD GPUs but has issues on integrated APUs:
The ipc: host and seccomp:unconfined are necessary for ROCm to function properly. The depends_on with service_healthy ensures the scanner waits for vLLM to be fully loaded before starting — important since model loading can take 2-3 minutes.
The confidence_threshold: 0.75 is crucial. Without it, the model reports every minor style issue. At 75%, it focuses on things it's genuinely concerned about.
The review_threshold: 5 triggers a different behavior: if the model finds more than 5 issues, it creates a single summary ticket for manual review rather than flooding JIRA with individual tickets. This is a safety valve for when the model goes haywire.
Structured Outputs with Pydantic
LLMs are great at finding issues but terrible at formatting output consistently. Left to their own devices, they'll return findings as markdown, prose, JSON with missing fields, or creative combinations thereof.
The solution is structured outputs. I define Pydantic models for exactly what I expect:
classSeverity(str,Enum):CRITICAL="critical"HIGH="high"MEDIUM="medium"LOW="low"INFO="info"classFindingType(str,Enum):BUG="bug"PERFORMANCE="performance"SECURITY="security"CODE_QUALITY="code_quality"POTENTIAL_ISSUE="potential_issue"classCodeFinding(BaseModel):file_path:str=Field(description="Path to the file")line_start:int=Field(description="Starting line number")line_end:Optional[int]=Field(default=None)finding_type:FindingTypeseverity:Severitytitle:str=Field(max_length=100)description:strsuggestion:Optional[str]=Noneconfidence:float=Field(ge=0.0,le=1.0)code_snippet:Optional[str]=None
The confidence field is a float between 0 and 1. The model learns to be honest about uncertainty — "I think this might be a bug (0.6)" versus "This is definitely division by zero (0.95)."
In a perfect world, I'd use vLLM's Outlines integration for guided JSON generation. In practice, I found that prompting Qwen for JSON and parsing the response works reliably:
def_analyze_code(self,file_path:str,content:str)->List[CodeFinding]:messages=[{"role":"system","content":self.system_prompt},{"role":"user","content":f"""Analyze this code for bugs and issues.File: {file_path}
{content}
ReturnaJSONarrayoffindings.Eachfindingmusthave:-file_path:string-line_start:number-finding_type:"bug"|"performance"|"security"|"code_quality"-severity:"critical"|"high"|"medium"|"low"|"info"-title:string(max100chars)-description:string-suggestion:stringornull-confidence:number0-1Ifnoissuesfound,returnanemptyarray:[]"""} ] response = self._call_llm(messages) # Parse JSON from response (handles markdown code blocks too) if response.strip().startswith('['): findings_data = json.loads(response) elif '```json' in response: json_str = response.split('```json')[1].split('```')[0] findings_data = json.loads(json_str) elif '[' in response: start = response.index('[') end = response.rindex(']') + 1 findings_data = json.loads(response[start:end]) else: return [] # Validate each finding with Pydantic findings = [] for item in findings_data: try: finding = CodeFinding(item) findings.append(finding) except ValidationError: pass # Skip malformed findings return findings
The System Prompt
The system prompt is where you teach the model what you care about. Here's mine:
You are an expert code reviewer specializing in Rust and Python.
Your job is to find bugs, performance issues, security vulnerabilities,
and code quality problems.
You are analyzing code from a ballistics calculation project that includes:
- A Rust physics engine for trajectory calculations
- Python Flask API with ML models
- PyO3 bindings between Rust and Python
Key areas to focus on:
1. Numerical precision issues (floating point errors, rounding)
2. Edge cases in physics calculations (division by zero, negative values)
3. Memory safety in Rust code
4. Error handling (silent failures, unwrap panics)
5. Performance bottlenecks (unnecessary allocations, redundant calculations)
6. Security issues (input validation, injection vulnerabilities)
Be conservative with findings - only report issues you are confident about.
Avoid false positives.
The phrase "Be conservative with findings" is doing heavy lifting. Without it, the model reports everything that looks slightly unusual. With it, it focuses on actual problems.
Timeout Handling
Large files (500+ lines) can take a while to analyze. My initial 120-second timeout caused failures on complex files. I bumped it to 600 seconds (10 minutes):
I also truncate files to 300 lines. For longer files, the model only sees the first 300 lines. This is a trade-off — I might miss bugs in the back half of long files — but it keeps scans predictable and prevents timeout cascades. I plan to revisit this in future iterations.
lines=content.split('\n')iflen(lines)>300:content='\n'.join(lines[:300])logger.info("Truncated to 300 lines for analysis")
JIRA Integration
When the scanner finds issues, it creates JIRA tickets automatically. The API is straightforward:
defcreate_jira_tickets(self,findings:List[CodeFinding]):jira_base_url=f"https://{jira_domain}/rest/api/3"forfindinginfindings:# Map severity to JIRA prioritypriority_map={Severity.CRITICAL:"Highest",Severity.HIGH:"High",Severity.MEDIUM:"Medium",Severity.LOW:"Low",Severity.INFO:"Lowest"}payload={"fields":{"project":{"key":"MBA"},"summary":f"[AI] {finding.title}","description":{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"text","text":build_description(finding)}]}]},"issuetype":{"name":"Bug"iffinding.finding_type==FindingType.BUGelse"Task"},"priority":{"name":priority_map[finding.severity]},"labels":["ai-detected","code-scanner"]}}response=requests.post(f"{jira_base_url}/issue",json=payload,auth=(jira_email,jira_api_key),headers={"Content-Type":"application/json"})
The [AI] prefix in the summary makes it obvious these tickets came from the scanner. The ai-detected label allows filtering.
I add a 2-second delay between ticket creation to avoid rate limiting:
time.sleep(2)# Rate limit protection
Systemd Scheduling
The scanner runs nightly via systemd timer:
# /etc/systemd/system/code-scanner.timer[Unit]Description=Run Code Scanner nightly at 11pm[Timer]OnCalendar=*-*-* 23:00:00Persistent=trueRandomizedDelaySec=300[Install]WantedBy=timers.target
The RandomizedDelaySec=300 adds up to 5 minutes of random delay. This prevents the scanner from always starting at exactly 11:00:00, which helps if multiple services share the same schedule.
The service unit is a oneshot that runs the scanner script:
The TimeoutStartSec=25200 (7 hours) gives the scanner enough time to complete even if it scans every file.
Sample Findings
Here's what the scanner actually finds. From a recent run:
{"file_path":"/home/alex/projects/ballistics-engine/src/fast_trajectory.rs","line_start":115,"finding_type":"bug","severity":"high","title":"Division by zero in fast_integrate when velocity approaches zero","description":"The division dt / velocity_magnitude could result in division by zero if the projectile stalls (velocity_magnitude = 0). This can happen at the apex of a high-angle shot.","suggestion":"Add a check for velocity_magnitude < epsilon before division, or clamp to a minimum value.","confidence":0.85}
This is a real issue. In ballistics calculations, a projectile fired at a high angle momentarily has zero horizontal velocity at the apex. Without a guard, this causes a panic.
Not every finding is valid. The model occasionally flags intentional design decisions as "issues." But at a 75% confidence threshold, the false positive rate is manageable — maybe 1 in 10 findings needs to be closed as "not a bug."
Trade-offs and Lessons
What works well:
- Finding numerical edge cases (division by zero, overflow)
- Spotting unwrap() calls on Options that might be None
- Identifying missing error handling
- Flagging dead code and unreachable branches
What doesn't work as well:
- Understanding business logic (the model doesn't know physics)
- Spotting subtle race conditions in concurrent code
- False positives on intentional patterns
Operational lessons:
- Start with a low iteration limit (10-20 files) to test the pipeline
- Monitor the first few runs manually before trusting it
- Keep credentials in .env files excluded from rsync
- The 300-line truncation is aggressive; consider chunking for long files
Handling JSON Parse Failures
Despite asking for JSON, LLMs sometimes produce malformed output. I see two failure modes:
Truncated JSON: The model runs out of tokens mid-response, leaving an unterminated string or missing closing brackets.
Wrapped JSON: The model adds explanatory text around the JSON, like "Here are the findings:" before the array.
My parser handles both:
defparse_findings_response(response:str)->list:"""Extract JSON from potentially messy LLM output."""response=response.strip()# Best case: raw JSON arrayifresponse.startswith('['):try:returnjson.loads(response)exceptjson.JSONDecodeError:pass# Fall through to extraction# Common case: JSON in markdown code blockif'```json'inresponse:try:json_str=response.split('```json')[1].split('```')[0]returnjson.loads(json_str)except(IndexError,json.JSONDecodeError):pass# Fallback: extract JSON array from surrounding textif'['inresponseand']'inresponse:try:start=response.index('[')end=response.rindex(']')+1returnjson.loads(response[start:end])exceptjson.JSONDecodeError:pass# Give uplogger.warning("Could not extract JSON from response")return[]
When parsing fails, I log the error and skip that file rather than crashing the entire scan. In a typical 50-file run, I see 2-3 parse failures — annoying but acceptable.
Testing the Pipeline
Before trusting the scanner with JIRA ticket creation, I ran it in "dry run" mode:
# Set max iterations low and disable JIRAexportMAX_ITERATIONS=5# In config: jira.enabled: false
pythonrun_scanner_direct.py
This scans just 5 files and prints findings without creating tickets. I manually reviewed each finding:
True positive: Division by zero in trajectory calculation — good catch
False positive: Flagged intentional unwrap() on a guaranteed-Some Option — needs better context
True positive: Dead code path never executed — valid cleanup suggestion
Marginal: Style suggestion about variable naming — below my quality threshold
After tuning the confidence threshold and system prompt, the true positive rate improved to roughly 90%.
Monitoring and Observability
The scanner writes detailed logs to stdout and a JSON results file. Sample log output:
I keep the last 30 result files (configurable) for historical comparison. Eventually I'll build a dashboard showing finding trends over time.
What's Next
The current system is batch-oriented: run once per night, file tickets, done. Future improvements I'm considering:
Pre-commit integration: Run on changed files only, fast enough for CI
Retrieval-augmented context: Include related files when analyzing (e.g., when scanning a function, include its callers)
Learning from feedback: Track which tickets get closed as "not a bug" and use that to tune prompts
Multi-model ensemble: Run the same code through two models, only file tickets when both agree
For now, though, the simple approach works. Every morning I check JIRA, triage the overnight findings, and fix the real bugs. The model isn't perfect, but it finds things I miss. And unlike a human reviewer, it never gets tired, never skips files, and never has a bad day.
Get the Code
I've open-sourced the complete scanner implementation on GitHub: llm-code-scanner
The project includes:
Dual scanning modes: Fast nightly scans via vLLM and comprehensive weekly analyses through Ollama
Smart deduplication: SQLite database prevents redundant issue tracking across runs
JIRA integration: Automatically creates tickets for findings above your confidence threshold
Email reports: SendGrid integration for daily/weekly summaries
Multi-language support: Python, Rust, TypeScript, Kotlin, Swift, Go, and more
To get started, clone the repo, configure your scanner_config.yaml with your vLLM/Ollama server details, and run python -m agent.scanner. The README has full setup instructions including environment variables for JIRA and SendGrid integration.
When developing ballistics-engine, a high-performance ballistics calculation library written in Rust, I faced a challenge: how do I efficiently build and distribute binaries for multiple operating systems and architectures? The answer led to the creation of an automated build orchestration system that leverages diverse hardware—from single-board computers to powerful x86_64 servers—to build native binaries for macOS, Linux, FreeBSD, NetBSD, and OpenBSD across both ARM64 and x86_64 architectures. Now, you are probably wondering why I am bothering to show love for the BSD Trilogy; the answer is simple: because I want to. Sure they are a bit esoteric, but I ran FreeBSD for years as my mail server. I still like the BSDs.
This article explores the architecture, implementation, and lessons learned from building a production-grade multi-platform build system that powers https://ballistics.zip, where users can download pre-built binaries for their platform with a simple curl command.
curl --proto '=https' --tlsv1.2 -sSf https://ballistics.zip/install.sh | sh
The Problem: Cross-Platform Distribution
Rust's cross-compilation capabilities are impressive, but they have limitations:
Cross-compilation complexity: While Rust supports cross-compilation, getting it working reliably for BSD systems (especially with system dependencies) is challenging
Native testing: You need to test on actual hardware to ensure binaries work correctly
Binary compatibility: Different BSD versions and configurations require native builds
Performance verification: Emulated builds may behave differently than native ones
The solution? Build natively on each target platform using actual hardware or high-performance emulation.
Architecture Overview
The build orchestration system consists of three main components:
1. Build Nodes (Physical and Virtual Machines)
macOS systems (x86_64 and aarch64) - Local builds
Linux x86_64 server - Remote build via SSH
FreeBSD ARM64 - Single-board computer (Raspberry Pi 4)
OpenBSD ARM64 - QEMU VM emulated on x86_64 (rig.localnet)
NetBSD x86_64 and ARM64 - QEMU VMs
2. Orchestrator (Python-based coordinator)
Reads build node configuration from build-nodes.yaml
Executes builds in parallel across all nodes
Collects artifacts via SSH/SCP
Generates SHA256 checksums
Uploads to Google Cloud Storage
Updates version metadata
3. Distribution (ballistics.zip website)
Serves install script at https://ballistics.zip
Hosts binaries in GCS bucket (gs://ballistics-releases/)
Provides version detection and automatic downloads
Supports version fallback for platforms with delayed releases
Role: Linux builds, BSD VM host, emulated ARM64 builds
CPU: Intel i9
RAM: 96GB
IP: 10.1.1.27 (Linux host), 10.1.1.17 (KVM host)
VMs Hosted:
FreeBSD x86_64: 10.1.1.21
OpenBSD x86_64: 10.1.1.20
OpenBSD ARM64 (emulated): 10.1.1.23
NetBSD x86_64: 10.1.1.19
Local macOS Development Machine
Role: macOS binary builds (both architectures)
Build Method: Local cargo builds with target flags
Architectures:
aarch64-apple-darwin (Apple Silicon)
x86_64-apple-darwin (Intel Macs)
A Surprising Discovery: Emulated ARM64 Performance
One of the most interesting findings during development was discovering that emulated ARM64 builds on powerful x86_64 hardware are significantly faster than emulated ARM64 on native ARM64 builds on single-board computers.
Performance Comparison
Emulated ARM64 on ARM64: ~99+ minutes per build
Emulated ARM64 on x86_64: 15m 37s ⚡
The emulated build on rig.localnet (running QEMU with KVM acceleration) completed in about 6x less time than the native ARM64 hardware. This is because:
The x86_64 server has significantly more powerful CPU cores
QEMU with KVM provides near-native performance for many workloads
Rust compilation is primarily CPU-bound and benefits from faster single-core performance
The x86_64 server has faster storage (NVMe vs eMMC/SD card)
As a result, the native OpenBSD ARM64 node on the Orange Pi is now disabled in favor of the emulated version.
Prerequisites
SSH Key-Based Authentication
Critical: The orchestration system requires passwordless SSH access to all remote build nodes. Here's how to set it up:
Generate SSH key (if you don't have one):
ssh-keygen-ted25519-C"build-orchestrator"
Copy public key to each build node:
# For each build node
ssh-copy-iduser@build-node-ip
# Examples:
ssh-copy-idalex@10.1.1.27# Linux x86_64
ssh-copy-idfreebsd@10.1.1.7# FreeBSD ARM64
ssh-copy-idroot@10.1.1.20# OpenBSD x86_64
ssh-copy-idroot@10.1.1.23# OpenBSD ARM64 emulated
ssh-copy-idroot@10.1.1.19# NetBSD x86_64
ssh-copy-idroot@10.1.1.15# NetBSD ARM64
Test SSH access:
sshuser@build-node-ip"uname -a"
Software Requirements
On Build Orchestrator Machine:
Python 3.8+
pyyaml (pip install pyyaml)
Google Cloud SDK (gcloud command) for GCS uploads
SSH client
On Each Build Node:
Rust toolchain (cargo, rustc)
Build essentials (compiler, linker)
curl, wget, or ftp (for downloading source)
Sufficient disk space (~2GB for build artifacts)
BSD-Specific Requirements
NetBSD: Install curl via pkgsrc (native ftp doesn't support HTTPS)
The orchestrator.py script coordinates the entire build process:
Step 1: Parallel Build Execution
defbuild_on_node(node,version):ifnode['host']=='local':# Local buildsubprocess.run(build_command,shell=True,check=True)else:# Remote build via SSHssh_command=f"ssh {node['host']} '{build_command}'"subprocess.run(ssh_command,shell=True,check=True)
Step 2: Artifact Collection
defcollect_artifacts(node,version):binary_name=f"ballistics-{version}-{node['name']}"ifnode['host']=='local':shutil.copy(node['binary_path'],f"./{binary_name}")else:# Download via SCPscp_command=f"scp {node['host']}:{node['binary_path']} ./{binary_name}"subprocess.run(scp_command,shell=True,check=True)
The easiest way to add a new node is using the interactive script:
cdbuild-orchestrator
./add-node.sh
This will prompt you for:
- Node name (e.g., openbsd-aarch64-emulated)
- SSH host (e.g., root@10.1.1.23 or local)
- Rust target triple (e.g., aarch64-unknown-openbsd)
- Build commands (how to download and build)
- Binary location (where the compiled binary is located)
Manual Configuration
Alternatively, edit build-nodes.yaml directly:
-name:your-new-platformhost:user@ip-address# or 'local' for local buildstarget:rust-target-triplebuild_command:|# Commands to download source and buildcd /tmp && rm -rf ballistics-engine-{version}curl -L -o v{version}.tar.gz https://github.com/...tar xzf v{version}.tar.gzcd ballistics-engine-{version}cargo build --releasebinary_path:/path/to/compiled/binaryenabled:true
Variables:
- {version}: Replaced with target version (e.g., 0.13.4)
- {target}: Replaced with Rust target triple
sudocat>/etc/systemd/system/openbsd-arm64-emulated-vm.service<< 'EOF'[Unit]Description=OpenBSD ARM64 VM (Emulated on x86_64)After=network.target[Service]Type=simpleUser=alexWorkingDirectory=/opt/bsd-vms/openbsd-arm64-emulatedExecStart=/opt/bsd-vms/openbsd-arm64-emulated/boot.shRestart=alwaysRestartSec=10[Install]WantedBy=multi-user.targetEOF
sudosystemctlenableopenbsd-arm64-emulated-vm.service
sudosystemctlstartopenbsd-arm64-emulated-vm.service
Configure networking (assign static IP 10.1.1.23)
Install build tools inside VM:
sshroot@10.1.1.23
pkg_addrustgit
Test SSH access:
sshroot@10.1.1.23"cargo --version"
Add to build-nodes.yaml and test:
./build.sh--version0.13.3--dry-run
GitHub Webhook Integration (Optional)
For fully automated builds triggered by GitHub releases:
SBCs are viable: Raspberry Pi and Orange Pi work well for native builds, but slower
Parallel execution: Running all 7 builds in parallel takes only ~16 minutes (longest pole is FreeBSD ARM64)
Conclusion
Building a custom multi-platform build orchestration system may seem daunting, but the benefits are substantial:
→ Full control: Own your build infrastructure
→ Native builds: Real hardware ensures compatibility
→ Cost-effective: Low operational costs after initial hardware investment
→ Fast iteration: Parallel builds complete in ~16 minutes
→ Flexibility: Easy to add new platforms
→ Learning: Deep understanding of cross-platform development
The surprising discovery that emulated ARM64 on powerful x86_64 hardware outperforms native ARM64 single-board computers has practical implications: you don't always need native hardware for every architecture. Strategic use of emulation can provide better performance while maintaining compatibility.
For projects requiring broad platform support (especially BSD systems not well-served by traditional CI/CD), this approach offers a reliable, maintainable, and cost-effective solution.
The Rockchip RK3588 has emerged as one of the most compelling ARM System-on-Chips (SoCs) for edge AI applications in 2024-2025, featuring a dedicated 6 TOPS Neural Processing Unit (NPU) integrated alongside powerful Cortex-A76/A55 CPU cores. This SoC powers a growing ecosystem of single-board computers and system-on-modules from manufacturers worldwide, including Orange Pi, Radxa, FriendlyElec, Banana Pi, and numerous industrial board makers.
But how does the RK3588's NPU perform in real-world scenarios? In this comprehensive deep dive, I'll share detailed benchmarks of the RK3588 NPU testing both Large Language Models (LLMs) and computer vision workloads, with primary testing on the Orange Pi 5 Max and comparative analysis against the closely-related RK3576 found in the Banana Pi CM5-Pro.
The RK3588 Ecosystem: Devices and Availability
The Rockchip RK3588 powers a diverse range of single-board computers (SBCs) and system-on-modules (SoMs) from multiple manufacturers in 2024-2025:
Consumer SBCs:
Orange Pi 5 Max - Full-featured SBC with up to 16GB RAM, M.2 NVMe, WiFi 6
Radxa ROCK 5B/5B+ - Available with up to 32GB RAM, PCIe 3.0, 8K video output
Boardcon Idea3588 - Compute module with up to 16GB RAM and 256GB eMMC
Theobroma Systems TIGER/JAGUAR - High-reliability modules for robotics and industrial automation
Recent Developments:
RK3588S2 (2024-2025) - Updated variant with modernized memory controllers and platform I/O while maintaining the same 6 TOPS NPU performance
The RK3576, found in devices like the Banana Pi CM5-Pro, shares the same 6 TOPS NPU architecture as the RK3588 but features different CPU cores (Cortex-A72/A53 vs. A76/A55), making it an interesting comparison point for NPU-focused workloads.
Hardware Overview
RK3588 SoC Specifications
Built on an 8nm process, the Rockchip RK3588 integrates:
CPU:
4x ARM Cortex-A76 @ 2.4 GHz (high-performance cores)
LLM tokens ≈ 0.75 words on average (1.33 tokens per word)
10-15 tokens/sec = ~7.5-11.25 words/second
Performance Analysis:
✅ 2-4x faster than reading aloud (2.5-2.7 words/sec)
✅ 2-3x faster than comfortable silent reading (3.3-5 words/sec)
✅ Comparable to speed reading (6.7-11.7 words/sec)
Verdict: The RK3588 NPU running TinyLlama 1.1B generates text significantly faster than most humans can comfortably read, making it well-suited for real-time conversational AI, chatbots, and interactive applications at the edge.
This is particularly impressive for a $180 device consuming only 5-6W of power. Users won't be waiting for the AI to "catch up" - instead, the limiting factor is human reading speed, not the NPU's generation capability.
Output Quality Verification
To verify the model produces meaningful, coherent responses, I tested it with several prompts:
Test 1: Factual Question
Prompt: "What is the capital of France?"
Response: "The capital of France is Paris."
✅ Result: Correct and concise answer.
Test 2: Simple Math
Prompt: "What is 2 plus 2?"
Response: "2 + 2 = 4"
✅ Result: Correct mathematical calculation.
Test 3: List Generation
Prompt:"List 3 colors: red,"Response:"Here are three different color options for your text:1. Red2. Orange3.Yellow"
✅ Result: Logical completion with proper formatting.
Observations:
Responses are coherent and grammatically correct
Factual accuracy is maintained after W8A8 quantization
The model understands context and provides relevant answers
Text generation is fluent and natural
No obvious degradation from quantization
Note: The interactive demo tends to continue generating after the initial response, sometimes repeating patterns. This appears to be a demo interface issue rather than a model quality problem - the initial responses to each prompt are consistently accurate and useful.
LLM Findings
Strengths:
Fast model conversion (~1.3 minutes for 1.1B model)
Text generation faster than human reading speed (7.5-11.25 words/sec)
All 3 NPU cores actively utilized
No noticeable quality degradation from W8A8 quantization
Limitations:
RK3588 only supports W8A8 quantization (no W4A16 for better compression)
1.14 GB model size may be limiting for memory-constrained deployments
Max context length: 2048 tokens
RK3588 vs RK3576: NPU Performance Comparison
The RK3576, found in the Banana Pi CM5-Pro, shares the same 6 TOPS NPU architecture as the RK3588 but differs in CPU configuration (Cortex-A72/A53 vs. A76/A55). This provides an interesting comparison for understanding NPU-specific performance versus overall platform capabilities.
W4A16 models are smaller (645MB vs 1.14GB for TinyLlama) but may run slower on some models
The NPU architecture is fundamentally the same (6 TOPS, 3 cores), but software stack differences affect performance
For 0.5B models, RK3588 shows ~20% better performance
Larger models benefit from W4A16's memory efficiency on RK3576
Computer Vision Performance:
Both RK3588 and RK3576 share the same NPU architecture for computer vision workloads:
MobileNet V1 on RK3576 (Banana Pi CM5-Pro): ~161.8ms per image (~6.2 FPS)
ResNet18 on RK3588 (Orange Pi 5 Max): 4.09ms per image (244 FPS)
The dramatic performance difference here is primarily due to model complexity (ResNet18 is better optimized for NPU execution than older MobileNet V1) rather than NPU hardware differences.
Practical Implications:
For NPU-focused workloads, both the RK3588 and RK3576 deliver similar AI acceleration capabilities. The choice between platforms should be based on:
CPU performance needs: RK3588's A76 cores are significantly faster
Quantization requirements: RK3576 offers W4A16 for LLMs, RK3588 only W8A8
Model size constraints: W4A16 (RK3576) produces smaller models
Cost considerations: RK3576 platforms (like CM5-Pro at $103) vs RK3588 platforms ($150-180)
PyTorch/TensorFlow models cannot execute directly on the NPU. They must be converted through an AOT (Ahead-of-Time) compilation process. However, this conversion is fast and straightforward.
Conversion Performance (x86_64)
Converting PyTorch ResNet18 to RKNN format:
Phase
Time
Size
Details
PyTorch → ONNX
0.25s
44.6 MB
Fixed batch size, opset 11
ONNX → RKNN
1.11s
-
INT8 quantization, operator fusion
Export
0.00s
11.4 MB
Final .rknn file
Total
1.37s
11.4 MB
25.7% of ONNX size
Model Optimizations:
INT8 quantization (weights and activations)
Automatic operator fusion
Layout optimization for NPU
Target: 3 NPU cores on RK3588
Memory Usage:
Internal memory: 1.1 MB
Weight memory: 11.5 MB
Total model size: 11.4 MB
NPU Inference Performance
Running ResNet18 inference on Orange Pi 5 Max (10 iterations after 2 warmup runs):
Results:
Average Inference Time: 4.09 ms
Min Inference Time: 4.02 ms
Max Inference Time: 4.43 ms
Standard Deviation: ±0.11 ms
Throughput: 244.36 FPS
Initialization Overhead:
NPU initialization: 0.350s (one-time)
Model load: 0.008s (one-time)
Input/Output:
Input: 224×224×3 images (INT8)
Output: 1000 classes (Float32)
Performance Comparison
Platform
Inference Time
Throughput
Notes
RK3588 NPU
4.09 ms
244 FPS
3 NPU cores, INT8
ARM A76 CPU (est.)
~50 ms
~20 FPS
Single core
Desktop RTX 3080
~2-3 ms
~400 FPS
Reference
NPU Speedup
12x faster than CPU
-
Same hardware
Computer Vision Findings
Strengths:
Extremely fast conversion (<2 seconds)
Excellent inference performance (4.09ms, 244 FPS)
Very consistent latency (±0.11ms)
Efficient quantization (74% size reduction)
12x speedup vs CPU cores on same SoC
Simple Python API for inference
Trade-offs:
INT8 quantization may reduce accuracy slightly
AOT conversion required (no dynamic model execution)
Fixed input shapes required
Technical Deep Dive
NPU Architecture
The RK3588 NPU is based on a 3-core design with 6 TOPS total performance:
The RK3588 NPU on the Orange Pi 5 Max delivers impressive performance for edge AI applications. With 244 FPS for ResNet18 (4.09ms latency) and 10-15 tokens/second for 1.1B LLMs, it's well-positioned for real-time computer vision and small language model inference.
✅ Good LLM support: 1B-class models run at usable speeds
✅ Outstanding value: $180 for 6 TOPS of NPU performance
✅ Easy to use: Simple Python API, automatic NPU detection
✅ Power efficient: ~5-6W under AI load, 39x better than desktop GPU
✅ PyTorch compatible: Via conversion workflow
⚠️ Conversion required: Cannot run PyTorch/TensorFlow directly
⚠️ Quantization needed: INT8 for best performance
⚠️ Memory constrained: Large models (>2GB) challenging
The RK3588 NPU is an excellent choice for edge AI applications where power efficiency and cost matter. It's not going to replace high-end GPUs for training or large-scale inference, but for deploying computer vision models and small LLMs at the edge, it's one of the best options available today.
The Banana Pi CM5-Pro (also sold as the ArmSoM-CM5) represents Banana Pi's entry into the Raspberry Pi Compute Module 4 form factor market, powered by Rockchip's RK3576 SoC. Released in 2024, this compute module targets developers seeking a CM4-compatible solution with enhanced specifications: up to 16GB of RAM, 128GB of storage, WiFi 6 connectivity, and a 6 TOPS Neural Processing Unit for AI acceleration. With a price point of approximately $103 for the 8GB/64GB configuration and a guaranteed production life until at least August 2034, Banana Pi positions the CM5-Pro as a long-term alternative to Raspberry Pi's official offerings.
After extensive testing, benchmarking, and comparison against contemporary single-board computers including the Orange Pi 5 Max, Raspberry Pi 5, and LattePanda IOTA, the Banana Pi CM5-Pro emerges as a competent but not exceptional offering. It delivers solid performance, useful features including AI acceleration, and good expandability, but falls short of being a clear winner in any specific category. This review examines where the CM5-Pro excels, where it disappoints, and who should consider it for their projects.
Banana Pi CM5-Pro showing the dual 100-pin connectors and CM4-compatible form factor
Hardware Architecture: The Rockchip RK3576
At the heart of the Banana Pi CM5-Pro lies the Rockchip RK3576, a second-generation 8nm SoC featuring a big.LITTLE ARM architecture:
4x ARM Cortex-A72 cores @ 2.2 GHz (high performance)
4x ARM Cortex-A53 cores @ 1.8 GHz (power efficiency)
The Cortex-A72, originally released by ARM in 2015, represents a significant step up from the ancient Cortex-A53 (2012) but still trails the more modern Cortex-A76 (2018) found in Raspberry Pi 5 and Orange Pi 5 Max. The A72 offers approximately 1.8-2x the performance per clock compared to the A53, with better branch prediction, wider execution units, and more sophisticated memory prefetching. However, it lacks the A76's more advanced microarchitecture improvements and typically runs at lower clock speeds (2.2 GHz vs. 2.4 GHz for the A76 in the Pi 5).
The inclusion of four Cortex-A53 efficiency cores alongside the A72 performance cores gives the RK3576 a total of eight cores, allowing it to balance power consumption and performance. In practice, this means the system can handle background tasks and light workloads on the A53 cores while reserving the A72 cores for demanding applications. The big.LITTLE scheduler in the Linux kernel attempts to make intelligent decisions about which cores to use for which tasks, though the effectiveness varies depending on workload characteristics.
M.2 NVMe SSD support (our unit had a 932GB NVMe drive installed)
WiFi 6 (802.11ax) and Bluetooth 5.3
Gigabit Ethernet
HDMI 2.0 output supporting 4K@60fps
Multiple MIPI CSI camera interfaces
USB 3.0 and USB 2.0 interfaces via the 100-pin connectors
The LPDDR5 memory is a notable upgrade over the LPDDR4 found in many competing boards, offering higher bandwidth and better power efficiency. In our testing, memory bandwidth didn't appear to be a significant bottleneck for CPU-bound workloads, though applications that heavily stress memory subsystems (large dataset processing, video encoding, etc.) may benefit from the faster RAM.
The inclusion of both eMMC storage and M.2 NVMe support provides excellent flexibility. The eMMC serves as a reliable boot medium with consistent performance, while the NVMe slot allows for high-capacity, high-speed storage expansion. This dual-storage approach is superior to SD card-only solutions, which suffer from reliability issues and inconsistent performance.
WiFi 6 and Bluetooth 5.3 represent current-generation wireless standards, providing better performance and lower latency than the WiFi 5 found in older boards. For robotics applications, low-latency wireless communication can be crucial for remote control and telemetry, making this a meaningful upgrade.
The NPU: 6 TOPS of AI Potential
The RK3576's integrated 6 TOPS Neural Processing Unit is the CM5-Pro's headline AI feature, designed to accelerate machine learning inference workloads. The NPU supports multiple quantization formats (INT4/INT8/INT16/BF16/TF32) and can interface with mainstream frameworks including TensorFlow, PyTorch, MXNet, and Caffe through Rockchip's RKNN toolkit.
In our testing, we confirmed the presence of the NPU hardware at /sys/kernel/iommu_groups/0/devices/27700000.npu and verified that the RKNN runtime library (librknnrt.so) and server (rknn_server) were installed and accessible. To validate real-world NPU performance, we ran MobileNet V1 image classification inference tests using the pre-installed RKNN model.
NPU Inference Benchmarks - MobileNet V1:
Running 10 inference iterations on a 224x224 RGB image (bell.jpg), we measured consistent performance:
Average inference time: 161.8ms per image
Min/Max: 146ms to 172ms
Standard deviation: ~7.2ms
Throughput: ~6.2 frames per second
The model successfully classified test images with appropriate confidence scores across 1,001 ImageNet classes. The inference pipeline includes:
JPEG decoding and preprocessing
Image resizing and color space conversion
INT8 quantized inference on the NPU
FP16 output tensor postprocessing
This demonstrates that the NPU is fully functional and provides practical acceleration for computer vision workloads. The ~160ms inference time for MobileNet V1 is reasonable for edge AI applications, though more demanding models like YOLOv8 or larger classification networks would benefit from the full 6 TOPS capacity.
Rockchip's RKNN toolkit provides a development workflow that converts trained models into RKNN format for efficient execution on the NPU. The process involves:
Training a model using a standard framework (TensorFlow, PyTorch, etc.)
Exporting the model to ONNX or framework-specific format
Converting the model using rknn-toolkit2 on a PC
Quantizing the model to INT8 or other supported formats
Deploying the RKNN model file to the board
Running inference using RKNN C/C++ or Python APIs
This workflow is more complex than simply running a PyTorch or TensorFlow model directly, but the trade-off is significantly improved inference performance and lower power consumption compared to CPU-only execution. For applications like real-time object detection, the 6 TOPS NPU can deliver:
Face recognition: 240fps @ 1080p
Object detection (YOLO-based models): 50fps @ 4K
Semantic segmentation: 30fps @ 2K
These performance figures represent substantial improvements over CPU-based inference, making the NPU genuinely useful for edge AI applications. However, they also require investment in learning the RKNN toolchain, optimizing models for the specific NPU architecture, and managing the conversion pipeline as part of your development workflow.
RKLLM and Large Language Model Support:
To thoroughly test LLM capabilities, we performed end-to-end testing: model conversion on an x86_64 platform (LattePanda IOTA), transfer to the CM5-Pro, and NPU inference validation. RKLLM (Rockchip Large Language Model) toolkit enables running quantized LLMs on the RK3576's 6 TOPS NPU, supporting models including Qwen, Llama, ChatGLM, Phi, Gemma, InternLM, MiniCPM, and others.
LLM Model Conversion Benchmark:
We converted TinyLLAMA 1.1B Chat from Hugging Face format to RKLLM format using an Intel N150-powered LattePanda IOTA:
Total Conversion Time: 264.83 seconds (4.41 minutes)
Output File Size: 644.75 MB (increased from 505 MB due to RKNN format overhead)
The cross-platform requirement is important: RKLLM-Toolkit is distributed as x86_64-only Python wheels, so model conversion must be performed on an x86 PC or VM, not on the ARM-based CM5-Pro itself. Conversion time scales with model size and CPU performance - larger models on slower CPUs will take proportionally longer.
NPU LLM Inference Testing:
After transferring the converted model to the CM5-Pro, we successfully:
✓ Loaded the TinyLLAMA 1.1B model (645 MB) into RKLLM runtime
✓ Initialized NPU with 2-core configuration for W4A16 inference
✓ Verified token generation and text output
✓ Confirmed the model runs on NPU cores (not CPU fallback)
The RKLLM runtime v1.2.2 correctly identified the model configuration (W4A16, max_context=2048, 2 NPU cores) and enabled the Cortex-A72 cores [4,5,6,7] for host processing while the NPU handled inference.
Actual RK3576 LLM Performance (Official Rockchip Benchmarks):
Based on Rockchip's published benchmarks for the RK3576, small language models perform as follows:
For context, the RK3588 (with more powerful NPU) achieves 42.58 tokens/second for Qwen2 0.5B - about 1.85x faster than the RK3576.
Practical Assessment:
The 30-35 tokens/second achieved with 0.5B models is usable for offline chatbots, text classification, and simple Q&A applications, but would feel noticeably slow compared to cloud LLM APIs or GPU-accelerated solutions. Humans typically read at 200-300 words per minute (~50 tokens/second), so 35 tokens/second is borderline for comfortable real-time conversation. Larger models (1.8B+) drop to 13 tokens/second or less, which feels sluggish for interactive use.
The complete workflow (download model → convert on x86 → transfer to ARM → run inference) works as designed but requires infrastructure: an x86 machine or VM for conversion, network transfer for large model files (645 MB), and familiarity with Python environments and RKLLM APIs. For embedded deployments, this is acceptable; for rapid prototyping, it adds friction compared to cloud-based LLM solutions.
Compared to Google's Coral TPU (4 TOPS), the RK3576's 6 TOPS provides 1.5x more computational power, though the Coral benefits from more mature tooling and broader community support. Against the Horizon X3's 5 TOPS, the RK3576 offers 20% more capability with far better CPU performance backing it up. For serious AI workloads, NVIDIA's Jetson platforms (40+ TOPS) remain in a different performance class, but at significantly higher price points and power requirements.
To assess the Banana Pi CM5-Pro's CPU performance, we ran our standard Rust compilation benchmark: building a complex ballistics simulation engine with numerous dependencies from a clean state, three times, and averaging the results. This real-world workload stresses CPU cores, memory bandwidth, compiler performance, and I/O subsystems.
Banana Pi CM5-Pro Compilation Times:
Run 1: 173.16 seconds (2 minutes 53 seconds)
Run 2: 162.29 seconds (2 minutes 42 seconds)
Run 3: 165.99 seconds (2 minutes 46 seconds)
Average: 167.15 seconds (2 minutes 47 seconds)
For context, here's how the CM5-Pro compares to other contemporary single-board computers:
System
CPU
Cores
Average Time
vs. CM5-Pro
Orange Pi 5 Max
Cortex-A55/A76
8 (4+4)
62.31s
2.68x faster
Raspberry Pi CM5
Cortex-A76
4
71.04s
2.35x faster
LattePanda IOTA
Intel N150
4
72.21s
2.31x faster
Raspberry Pi 5
Cortex-A76
4
76.65s
2.18x faster
Banana Pi CM5-Pro
Cortex-A53/A72
8 (4+4)
167.15s
1.00x (baseline)
The results reveal the CM5-Pro's positioning: it's significantly slower than top-tier ARM and x86 single-board computers, but respectable within its price and power class. The 2.68x performance deficit versus the Orange Pi 5 Max is substantial, explained by the RK3588's newer Cortex-A76 cores running at higher clock speeds (2.4 GHz) with more advanced microarchitecture.
More telling is the comparison to the Raspberry Pi 5 and Raspberry Pi CM5, both featuring four Cortex-A76 cores at 2.4 GHz. Despite having eight cores to the Pi's four, the CM5-Pro is approximately 2.2x slower. This performance gap illustrates the generational advantage of the A76 architecture - the Pi 5's four newer cores outperform the CM5-Pro's four A72 cores plus four A53 cores combined for this workload.
The LattePanda IOTA's Intel N150, despite having only four cores, also outperforms the CM5-Pro by 2.3x. Intel's Alder Lake-N architecture, even in its low-power form, delivers superior single-threaded performance and more effective multi-threading than the RK3576.
However, context matters. The CM5-Pro's 167-second compilation time is still quite usable for development workflows. A project that takes 77 seconds to compile on a Raspberry Pi 5 will take 167 seconds on the CM5-Pro - an additional 90 seconds. For most developers, this difference is noticeable but not crippling. Compile times remain in the "get a coffee" range rather than the "go to lunch" range.
More importantly, the CM5-Pro vastly outperforms older ARM platforms. Compared to boards using only Cortex-A53 cores (like the Horizon X3 CM at 379 seconds), the CM5-Pro is 2.27x faster, demonstrating the value of the Cortex-A72 performance cores.
Geekbench 6 CPU Performance
To provide standardized synthetic benchmarks, we ran Geekbench 6.5.0 on the Banana Pi CM5-Pro:
Geekbench 6 Scores:
Single-Core Score: 328
Multi-Core Score: 1337
These scores reflect the RK3576's positioning as a mid-range ARM platform. The single-core score of 328 indicates modest per-core performance from the Cortex-A72 cores, while the multi-core score of 1337 demonstrates reasonable scaling across all eight cores (4x A72 + 4x A53). For context, the Raspberry Pi 5 with Cortex-A76 cores typically scores around 550-600 single-core and 1700-1900 multi-core, showing the generational advantage of the newer ARM architecture.
Notable individual benchmark results include:
PDF Renderer: 542 single-core, 2904 multi-core
Ray Tracer: 2763 multi-core
Asset Compression: 2756 multi-core
Horizon Detection: 540 single-core
HTML5 Browser: 455 single-core
The relatively strong performance on PDF rendering and asset compression tasks suggests the RK3576 handles real-world productivity workloads reasonably well, though the lower single-core scores indicate that latency-sensitive interactive applications may feel less responsive than on platforms with faster per-core performance.
Full Geekbench results: https://browser.geekbench.com/v6/cpu/14853854
Comparative Analysis: CM5-Pro vs. the Competition
vs. Orange Pi 5 Max
The Orange Pi 5 Max represents the performance leader in our testing, powered by Rockchip's flagship RK3588 SoC with four Cortex-A76 + four Cortex-A55 cores. The 5 Max compiled our benchmark in 62.31 seconds - 2.68x faster than the CM5-Pro's 167.15 seconds.
Key differences:
Performance: The 5 Max's Cortex-A76 cores deliver substantially better single-threaded and multi-threaded performance. For CPU-intensive development work, the performance gap is significant.
NPU: The RK3588 includes a 6 TOPS NPU, matching the RK3576's AI capabilities. Both boards can run similar RKNN-optimized models with comparable inference performance.
Form Factor: The 5 Max is a full-sized single-board computer with on-board ports and connectors, while the CM5-Pro is a compute module requiring a carrier board. This makes the 5 Max more suitable for standalone projects and the CM5-Pro better for embedded integration.
Price: The Orange Pi 5 Max sells for approximately \$150-180 with 8GB RAM, compared to $103 for the CM5-Pro. The 5 Max's superior performance comes at a premium, but the cost-per-performance ratio remains competitive.
Memory: Both support up to 16GB RAM, though the 5 Max typically ships with higher-capacity configurations.
Verdict: If raw CPU performance is your priority and you can accommodate a full-sized SBC, the Orange Pi 5 Max is the clear choice. The CM5-Pro makes sense if you need the compute module form factor, want to minimize cost, or have thermal/power constraints that favor the slightly more efficient RK3576.
vs. Raspberry Pi 5
The Raspberry Pi 5, with its Broadcom BCM2712 SoC featuring four Cortex-A76 cores at 2.4 GHz, compiled our benchmark in 76.65 seconds - 2.18x faster than the CM5-Pro.
Key differences:
Performance: The Pi 5's four A76 cores outperform the CM5-Pro's 4+4 big.LITTLE configuration for most workloads. Single-threaded performance heavily favors the Pi 5, while multi-threaded performance depends on whether the workload can effectively utilize the CM5-Pro's additional A53 cores.
NPU: The Pi 5 lacks integrated AI acceleration, while the CM5-Pro includes a 6 TOPS NPU. For AI-heavy applications, this is a significant advantage for the CM5-Pro.
Ecosystem: The Raspberry Pi ecosystem is vastly more mature, with extensive documentation, massive community support, and guaranteed long-term software maintenance. While Banana Pi has committed to supporting the CM5-Pro until 2034, the Pi Foundation's track record inspires more confidence.
Software: Raspberry Pi OS is polished and actively maintained, with hardware-specific optimizations. The CM5-Pro runs generic ARM Linux distributions (Debian, Ubuntu) which work well but lack Pi-specific refinements.
Price: The Raspberry Pi 5 (8GB model) retails for \$80, significantly cheaper than the CM5-Pro's \$103. The Pi 5 offers better performance for less money - a compelling value proposition.
Expansion: The Pi 5's standard SBC form factor provides easier access to GPIO, HDMI, USB, and other interfaces. The CM5-Pro requires a carrier board, adding cost and complexity but enabling more customized designs.
Verdict: For general-purpose computing, development, and hobbyist projects, the Raspberry Pi 5 is the better choice: faster, cheaper, and better supported. The CM5-Pro makes sense if you specifically need AI acceleration, prefer the compute module form factor, or want more RAM/storage capacity than the Pi 5 offers.
vs. LattePanda IOTA
The LattePanda IOTA, powered by Intel's N150 Alder Lake-N processor with four cores, compiled our benchmark in 72.21 seconds - 2.31x faster than the CM5-Pro.
Key differences:
Architecture: The IOTA uses x86_64 architecture, providing compatibility with a wider range of software that may not be well-optimized for ARM. The CM5-Pro's ARM architecture benefits from lower power consumption and better mobile/embedded software support.
Performance: Intel's N150, despite having only four cores, delivers superior single-threaded performance and competitive multi-threaded performance against the CM5-Pro's eight cores. Intel's microarchitecture and higher sustained frequencies provide an edge for CPU-bound tasks.
NPU: The IOTA lacks dedicated AI acceleration, relying on CPU or external accelerators for machine learning workloads. The CM5-Pro's integrated 6 TOPS NPU is a clear advantage for AI applications.
Power Consumption: The N150 is a low-power x86 chip, but still consumes more power than ARM solutions under typical workloads. The CM5-Pro's big.LITTLE configuration can achieve better power efficiency for mixed workloads.
Form Factor: The IOTA is a small x86 board with Arduino co-processor integration, targeting maker/IoT applications. The CM5-Pro's compute module format serves different use cases, primarily embedded systems and custom carrier board designs.
Price: The LattePanda IOTA sells for approximately $149, more expensive than the CM5-Pro. However, it includes unique features like the Arduino co-processor and x86 compatibility that may justify the premium for specific applications.
Software Ecosystem: x86 enjoys broader commercial software support, while ARM excels in embedded and mobile-focused applications. Choose based on your software requirements.
Verdict: If you need x86 compatibility or want a compact standalone board with Arduino integration, the LattePanda IOTA makes sense despite its higher price. If you're working in ARM-native embedded Linux, need AI acceleration, or want the compute module form factor, the CM5-Pro is the better choice at a lower price point.
vs. Raspberry Pi CM5
The Raspberry Pi Compute Module 5 is the most direct competitor to the Banana Pi CM5-Pro, offering the same CM4-compatible form factor with different specifications. The Pi CM5 compiled our benchmark in 71.04 seconds - 2.35x faster than the CM5-Pro.
Key differences:
Performance: The Pi CM5's four Cortex-A76 cores at 2.4 GHz significantly outperform the CM5-Pro's 4x A72 + 4x A53 configuration. The architectural advantage of the A76 over the A72 translates to approximately 2.35x better performance in our testing.
NPU: The CM5-Pro's 6 TOPS NPU provides integrated AI acceleration, while the Pi CM5 requires external solutions (Hailo-8, Coral TPU) for hardware-accelerated inference. If AI is central to your application, the CM5-Pro's integrated NPU is more elegant.
Memory Options: The CM5-Pro supports up to 16GB LPDDR5, while the Pi CM5 offers up to 8GB LPDDR4X. For memory-intensive applications, the CM5-Pro's higher capacity could be decisive.
Storage: Both offer eMMC options, with the CM5-Pro available up to 128GB and the Pi CM5 up to 64GB. Both support additional storage via carrier board interfaces.
Price: The Raspberry Pi CM5 (8GB/32GB eMMC) sells for approximately $95, slightly cheaper than the CM5-Pro's $103. The CM5-Pro's extra features (more RAM/storage options, integrated NPU) justify the small price premium for those who need them.
Ecosystem: The Pi CM5 benefits from Raspberry Pi's ecosystem, tooling, and community. The CM5-Pro has decent support but can't match the Pi's extensive resources.
Carrier Boards: Both are CM4-compatible, meaning they can use the same carrier boards. However, some boards may not fully support CM5-Pro-specific features, and subtle electrical differences could cause issues in rare cases.
Verdict: For maximum CPU performance in the CM4 form factor, choose the Pi CM5. Its 2.35x performance advantage is significant for compute-intensive applications. Choose the CM5-Pro if you need integrated AI acceleration, more than 8GB of RAM, more than 64GB of eMMC storage, or prefer the better wireless connectivity (WiFi 6 vs. WiFi 5).
Use Cases and Recommendations
Based on our testing and analysis, here are scenarios where the Banana Pi CM5-Pro excels and where alternatives might be better:
Choose the Banana Pi CM5-Pro if you:
Need AI acceleration in a compute module: The integrated 6 TOPS NPU eliminates the need for external AI accelerators, simplifying hardware design and reducing BOM costs. For robotics, smart cameras, or IoT devices with AI workloads, this is a compelling advantage.
Require more than 8GB of RAM: The CM5-Pro supports up to 16GB LPDDR5, double the Pi CM5's maximum. If your application processes large datasets, runs multiple VMs, or needs extensive buffering, the extra RAM headroom matters.
Want high-capacity built-in storage: With up to 128GB eMMC options, the CM5-Pro can store large datasets, models, or applications without requiring external storage. This simplifies deployment and improves reliability compared to SD cards or network storage.
Prefer WiFi 6 and Bluetooth 5.3: Current-generation wireless standards provide better performance and lower latency than WiFi 5. For wireless robotics control or IoT applications with many connected devices, WiFi 6's improvements are meaningful.
Value long production lifetime: Banana Pi's commitment to produce the CM5-Pro until August 2034 provides assurance for commercial products with multi-year lifecycles. You can design around this module without fear of it being discontinued in 2-3 years.
Have thermal or power constraints: The RK3576's 8nm process and big.LITTLE architecture can deliver better power efficiency than always-on high-performance cores, extending battery life or reducing cooling requirements for fanless designs.
Choose alternatives if you:
Prioritize raw CPU performance: The Raspberry Pi 5, Pi CM5, Orange Pi 5 Max, and LattePanda IOTA all deliver significantly faster CPU performance. If your application is CPU-bound and doesn't benefit from the NPU, these platforms are better choices.
Want the simplest development experience: The Raspberry Pi ecosystem's polish, documentation, and community support make it the easiest platform for beginners and rapid prototyping. The Pi 5 or Pi CM5 will get you running faster with fewer obstacles.
Need maximum AI performance: NVIDIA Jetson platforms provide 40+ TOPS of AI performance with mature CUDA/TensorRT tooling. If AI is your primary workload, the investment in a Jetson module is worthwhile despite higher costs.
Require x86 compatibility: The LattePanda IOTA or other x86 platforms provide better software compatibility for commercial applications that depend on x86-specific libraries or software.
Work with standard SBC form factors: If you don't need a compute module and prefer the convenience of a full-sized SBC with onboard ports, the Orange Pi 5 Max or Raspberry Pi 5 are better choices.
The NPU in Practice: RKNN Toolkit and Ecosystem
While we didn't perform exhaustive AI benchmarking, our exploration of the RKNN ecosystem reveals both promise and challenges. The infrastructure exists: the NPU hardware is present and accessible, the runtime libraries are installed, and documentation is available from both Rockchip and Banana Pi. The RKNN toolkit can convert mainstream frameworks to NPU-optimized models, and community examples demonstrate YOLO11n object detection running successfully on the CM5-Pro.
However, the RKNN development experience is not as streamlined as more mature ecosystems. Converting and optimizing models requires learning Rockchip-specific tools and workflows. Debugging performance issues or accuracy degradation during quantization demands patience and experimentation. The documentation is improving but remains fragmented across Rockchip's official site, Banana Pi's docs, and community forums.
For developers already familiar with embedded AI deployment, the RKNN workflow will feel familiar - it follows similar patterns to TensorFlow Lite, ONNX Runtime, or other edge inference frameworks. For developers new to edge AI, the learning curve is steeper than cloud-based solutions but gentler than some alternatives (looking at you, Hailo's toolchain).
The 6 TOPS performance figure is real and achievable for properly optimized models. INT8 quantized YOLO models can indeed run at 50fps @ 4K, and simpler models scale accordingly. The NPU's support for INT4 and BF16 formats provides flexibility for trading off accuracy versus performance. For many robotics and IoT applications, the 6 TOPS NPU hits a sweet spot: enough performance for useful AI workloads, integrated into the SoC to minimize complexity and cost, and accessible through reasonable (if not perfect) tooling.
Build Quality and Physical Characteristics
The Banana Pi CM5-Pro adheres to the Raspberry Pi CM4 mechanical specification, featuring dual 100-pin high-density connectors arranged in the standard layout. Physical dimensions match the CM4, allowing drop-in replacement in compatible carrier boards. Our sample unit appeared well-manufactured with clean solder joints, proper component placement, and no obvious defects.
The module includes an on-board WiFi/Bluetooth antenna connector (U.FL/IPEX), power management IC, and all necessary supporting components. Unlike some compute modules that require extensive external components on the carrier board, the CM5-Pro is relatively self-contained, simplifying carrier board design.
Thermal performance is adequate but not exceptional. Under sustained load during our compilation benchmarks, the SoC reached temperatures requiring thermal management. For applications running continuous AI inference or heavy CPU workloads, active cooling (fan) or substantial passive cooling (heatsink and airflow) is recommended. The carrier board design should account for thermal dissipation, especially if the module will be enclosed in a case.
Software and Ecosystem
The CM5-Pro ships with Banana Pi's custom Debian-based Linux distribution, featuring a 6.1.75 kernel with Rockchip-specific patches and drivers. In our testing, the system worked well out of the box: networking functioned, sudo worked (refreshingly, after the Horizon X3 CM disaster), and package management operated normally.
The distribution includes pre-installed RKNN libraries and tools, enabling NPU development without additional setup. Python 3 and essential development packages are available, and standard Debian repositories provide access to thousands of additional packages. For developers comfortable with Debian/Ubuntu, the environment feels familiar and capable.
However, the software ecosystem lags behind Raspberry Pi's. Raspberry Pi OS includes countless optimizations, hardware-specific integrations, and utilities that simply don't exist for Rockchip platforms. Camera support, GPIO access, and peripheral interfaces work, but often require more manual configuration or programming compared to the Pi's plug-and-play experience.
Third-party software support varies. Popular frameworks like ROS2, OpenCV, and TensorFlow compile and run without issues. Hardware-specific accelerators (GPU, NPU) may require additional configuration or custom builds. Overall, the software situation is "good enough" for experienced developers but not as polished as the Raspberry Pi ecosystem.
Banana Pi's documentation has improved significantly over the years, with reasonably comprehensive guides covering basic setup, GPIO usage, and RKNN deployment. Community support exists through forums and GitHub, though it's smaller and less active than Raspberry Pi's communities. Expect to do more troubleshooting independently and rely less on finding someone who's already solved your exact problem.
Conclusion: A Capable Platform for Specific Niches
The Banana Pi CM5-Pro is a solid, if unspectacular, compute module that serves specific niches well while falling short of being a universal recommendation. Its combination of integrated 6 TOPS NPU, up to 16GB RAM, WiFi 6 connectivity, and CM4-compatible form factor creates a unique offering that competes effectively against alternatives when your requirements align with its strengths.
For projects needing AI acceleration in a compute module format, the CM5-Pro is arguably the best choice currently available. The integrated NPU eliminates the complexity and cost of external AI accelerators while delivering genuine performance improvements for inference workloads. The RKNN toolkit, while imperfect, provides a workable path to deploying optimized models. If your robotics platform, smart camera, or IoT device depends on local AI processing, the CM5-Pro deserves serious consideration.
For projects requiring more than 8GB of RAM or more than 64GB of storage in a compute module, the CM5-Pro is the only game in town among CM4-compatible options. This makes it the default choice for memory-intensive applications that need the compute module form factor.
For general-purpose computing, development, or applications where AI is not central, the Raspberry Pi CM5 is the better choice. Its 2.35x performance advantage is substantial and directly translates to faster build times, quicker application responsiveness, and better user experience. The Pi's ecosystem advantages further tip the scales for most users.
Our compilation benchmark results - 167 seconds for the CM5-Pro versus 71-77 seconds for Pi5/CM5 - illustrate the performance gap clearly. For development workflows, this difference is noticeable but workable. Most developers can tolerate the CM5-Pro's slower compilation times if other factors (AI acceleration, RAM capacity, price) favor it. But if maximum CPU performance is your priority, look elsewhere.
The comparison to the Orange Pi 5 Max reveals a significant performance gap (62 vs. 167 seconds), but also highlights different market positions. The 5 Max is a full-featured SBC designed for standalone use, while the CM5-Pro is a compute module designed for embedded integration. They serve different purposes and target different applications.
Against the LattePanda IOTA's x86 architecture, the CM5-Pro trades x86 compatibility for better power efficiency, integrated AI, and lower cost. The choice between them depends entirely on software requirements - x86-specific applications favor the IOTA, while ARM-native embedded applications favor the CM5-Pro.
The Banana Pi CM5-Pro earns a qualified recommendation: excellent for AI-focused embedded projects, good for high-RAM compute module applications, acceptable for general embedded Linux development, and not recommended if raw CPU performance or ecosystem maturity are priorities. At $103 for the 8GB/64GB configuration, it offers reasonable value for applications that leverage its strengths, though it won't excite buyers seeking the fastest or cheapest option.
If your project needs:
AI acceleration integrated into a compute module
More than 8GB RAM in CM4 form factor
WiFi 6 and current wireless standards
Guaranteed long production life (until 2034)
Then the Banana Pi CM5-Pro is a solid choice that delivers on its promises.
If your project needs:
Maximum CPU performance
The most polished software ecosystem
The easiest development experience
The lowest cost
Then the Raspberry Pi CM5 or Pi 5 remains the better option.
The CM5-Pro occupies a middle ground: not the fastest, not the cheapest, not the easiest, but uniquely capable in specific areas. For the right application, it's exactly what you need. For others, it's a compromise that doesn't quite satisfy. Choose accordingly.
Recommendation: Good choice for AI-focused embedded projects requiring compute module form factor; not recommended if raw CPU performance is the priority.
Review Date: November 3, 2025
Hardware Tested: Banana Pi CM5-Pro (ArmSoM-CM5) with 4GB RAM, 29GB eMMC, 932GB NVMe SSD
OS Tested: Banana Pi Debian (based on Debian GNU/Linux), kernel 6.1.75
Conclusion: Solid middle-ground option with integrated AI acceleration; best for specific niches rather than general-purpose use.
The Horizon X3 CM (Compute Module) represents an interesting case study in the single-board computer market: a product marketed as an AI-focused robotics platform that, in practice, falls dramatically short of both its promises and its competition. Released during the 2021-2022 timeframe and based on Horizon Robotics' Sunrise 3 chip (announced September 2020), the X3 CM attempts to position itself as a robotics development platform with integrated AI acceleration through its "Brain Processing Unit" or BPU. However, as we discovered through extensive testing and configuration attempts, the Horizon X3 CM is an underwhelming offering that suffers from outdated hardware, broken software distributions, abandoned documentation, and a configuration process so Byzantine that it borders on hostile to users.
Horizon X3 CM compute module showing the CM4-compatible 200-pin connector
Horizon X3 CM installed on a carrier board with exposed components
Hardware Architecture: A Foundation Built on Yesterday's Technology
At the heart of the Horizon X3 CM lies the Sunrise X3 system-on-chip, featuring a quad-core ARM Cortex-A53 processor clocked at 1.5 GHz, paired with a single Cortex-R5 core for real-time tasks. The Cortex-A53, released by ARM in 2012, was already considered a low-power, efficiency-focused core at launch. By 2025 standards, it is ancient technology - predating even the Cortex-A55 by six years and the high-performance Cortex-A76 by eight years.
To put this in perspective: the Cortex-A53 was designed in an era when ARM was still competing against Intel Atom processors in tablets and smartphones. The microarchitecture lacks modern features like advanced branch prediction, sophisticated out-of-order execution, and the aggressive clock speeds found in contemporary ARM cores. It was never intended for computationally demanding workloads, instead optimizing for power efficiency in battery-powered devices.
The system includes 2GB or 4GB of RAM (our test unit had 4GB), eMMC storage options, and the typical suite of interfaces expected on a compute module: MIPI CSI for cameras, MIPI DSI for displays, USB 3.0, Gigabit Ethernet, and HDMI output. The physical form factor mimics the Raspberry Pi Compute Module 4's 200-pin board-to-board connector, allowing it to fit into existing CM4 carrier boards - at least in theory.
The BPU: Marketing Promise vs. Reality
The headline feature of the Horizon X3 CM is undoubtedly its Brain Processing Unit, marketed as providing 5 TOPS (trillion operations per second) of AI inference capability using Horizon's Bernoulli 2.0 architecture. The BPU is a dual-core dedicated neural processing unit fabricated on a 16nm process, designed specifically for edge AI applications in robotics and autonomous driving.
On paper, 5 TOPS sounds impressive for an edge device. The marketing materials emphasize the X3's ability to run AI models locally without cloud dependency, perform real-time object detection, enable autonomous navigation, and support various computer vision tasks. Horizon Robotics, founded in 2015 and focused primarily on automotive AI processors, positioned the Sunrise 3 chip as a way to bring their automotive-grade AI capabilities to the robotics and IoT markets.
In practice, the BPU's utility is severely constrained by several factors. First, the 5 TOPS figure assumes optimal utilization with models specifically optimized for the Bernoulli architecture. Second, the Cortex-A53 CPU cores create a significant bottleneck for any workload that cannot be entirely offloaded to the BPU. Third, and most critically, the toolchain and software ecosystem required to actually leverage the BPU is fragmented, poorly documented, and largely abandoned.
The Software Ecosystem: Abandonment and Fragmentation
Perhaps the most telling aspect of the Horizon X3 CM is the state of its software support. Horizon Robotics archived all their GitHub repositories, effectively abandoning public development and support. D-Robotics, which appears to be either a subsidiary or spin-off focused on the robotics market, has continued maintaining forks of some repositories, but the overall ecosystem feels scattered and undermaintained.
hobot_llm: An Exercise in Futility
One of the more recent developments is hobot_llm, a project that attempts to run Large Language Models on the RDK X3 platform. Hosted at https://github.com/D-Robotics/hobot_llm, this ROS2 node promises to bring LLM capabilities to edge robotics applications. The reality is far less inspiring.
hobot_llm provides two interaction modes: a terminal-based chat interface and a ROS2 node that subscribes to text topics and publishes LLM responses. The system requires the 4GB RAM version of the RDK X3 and recommends increasing the BPU reserved memory to 1.7GB - leaving precious little memory for other tasks.
Users report that responses take 15-30 seconds to generate, and the quality of responses is described as "confusing and mostly unrelated to the query." This performance characteristic makes the system effectively useless for any real-time robotics application. A robot that takes 30 seconds to formulate a language-based response is not demonstrating intelligence; it's demonstrating the fundamental inadequacy of the platform.
The hobot_llm project exemplifies the broader problem with the X3 ecosystem: projects that look interesting in concept but fall apart under scrutiny, implemented on hardware that lacks the computational resources to make them practical, maintained by a fractured development community that can't provide consistent support.
D-Robotics vs. Horizon Robotics: Corporate Confusion
The relationship between Horizon Robotics and D-Robotics adds another layer of confusion for potential users. Horizon Robotics, the original creator of the Sunrise chips, has clearly shifted its focus to the automotive market, where margins are higher and customers are more willing to accept proprietary, closed-source solutions. The company's GitHub repositories were archived, signaling an end to community-focused development.
D-Robotics picked up the robotics development kit mantle, maintaining forks of key repositories like hobot_llm, hobot_dnn (the DNN inference framework), and the RDK model zoo. However, this continuation feels more like life support than active development. Commit frequencies are low, issues pile up without resolution, and the documentation remains fragmented across multiple sites (d-robotics.cc, developer.d-robotics.cc, github.com/D-Robotics, github.com/HorizonRDK).
For a potential user in 2025, this corporate structure raises immediate red flags. Who actually supports this platform? If you encounter a problem, where do you file an issue? If Horizon has abandoned the project and D-Robotics is merely keeping it alive, what is the long-term viability of building a product on this foundation?
The Bootstrap Nightmare: A System Designed to Frustrate
If the hardware limitations and software abandonment weren't enough to dissuade potential users, the actual process of getting a functioning Horizon X3 CM system should seal the case. We downloaded the latest Ubuntu 22.04-derived distribution from https://archive.d-robotics.cc/downloads/en/os_images/rdk_x3/rdk_os_3.0.3-2025-09-08/ and discovered a system configuration so broken and non-standard that it defies belief.
The Sudo Catastrophe
The most egregious issue: sudo doesn't work out of the box. Not because of a configuration error, but because critical system files are owned by the wrong user. The distribution ships with /usr/bin/sudo, /etc/sudoers, and related files owned by uid 1000 (the sunrise user) rather than root. This creates an impossible catch-22:
You need root privileges to fix the file ownership
sudo is the standard way to gain root privileges
sudo won't function because of incorrect ownership
You can't fix the ownership without root privileges
Traditional escape routes all fail. The root password is not set, so su doesn't work. pkexec requires polkit authentication. systemctl requires authentication for privileged operations. Even setting file capabilities (setcap) to grant specific privileges fails because the sunrise user lacks CAP_SETFCAP.
The workaround involves creating an /etc/rc.local script that runs at boot time as root to fix ownership of sudo binaries, sudoers files, and apt directories:
This is not a minor configuration quirk. This is a fundamental misunderstanding of Linux system security and standard practices. No competent distribution would ship with sudo broken in this manner. The fact that this made it into a release image dated September 2025 suggests either complete incompetence or absolute indifference to user experience.
Network Configuration Hell
The default network configuration assumes you're using the 192.168.1.0/24 subnet with a gateway at 192.168.1.1. If your network uses any other addressing scheme - as most enterprise networks, lab environments, and even many home networks do - you're in for a frustrating experience.
Changing the network configuration should be trivial: edit /etc/network/interfaces, update the IP address and gateway, reboot. Except the sunrise user lacks CAP_NET_ADMIN capability, so you can't use ip commands to modify network configuration on the fly. You can't use NetworkManager's command-line tools without authentication. You must edit the configuration files manually and reboot to apply changes.
Our journey to move the device from 192.168.1.10 to 10.1.1.135 involved:
Accessing the device through a gateway system that could route to both networks
Backing up /etc/network/interfaces
Manually editing the static IP configuration
Removing conflicting secondary IP configuration scripts
Adding DNS servers (which weren't configured at all in the default image)
Rebooting and hoping the configuration took
Troubleshooting DNS resolution failures
Editing /etc/systemd/resolved.conf to add nameservers
Adding a systemd-resolved restart to /etc/rc.local
Rebooting again
This process, which takes approximately 30 seconds on a properly configured Linux system, consumed hours on the Horizon X3 CM due to the broken permissions structure and missing default configurations.
Repository Roulette
The default APT repositories point to mirrors.tuna.tsinghua.edu.cn (a Chinese university mirror) and archive.sunrisepi.tech (which is frequently unreachable). For users outside China, these repositories are slow or inaccessible. The solution requires manually reconfiguring /etc/apt/sources.list to use official Ubuntu Ports mirrors:
Again, this should be a non-issue. Modern distributions detect geographic location and configure appropriate mirrors automatically. The Horizon X3 CM requires manual intervention for basic package management functionality.
The Permission Structure Mystery
Beyond these specific issues lies a broader architectural decision that makes no sense: why are system directories owned by a non-root user? Running ls -ld on /etc, /usr/lib, and /var/lib/apt reveals they're owned by sunrise:sunrise rather than root:root. This violates fundamental Unix security principles and creates cascading problems throughout the system.
Was this an intentional design decision? If so, what was the rationale? Was it an accident that made it through quality assurance? The complete lack of documentation about this unusual setup suggests it's not intentional, yet it persists through multiple distribution releases.
Performance Testing: Confirmation of Inadequacy
To quantitatively assess the Horizon X3 CM's performance, we ran our standard Rust compilation benchmark: building a complex ballistics simulation engine with numerous dependencies from clean state, three times, and averaging the results. This workload stresses CPU cores, memory bandwidth, and compiler performance - a representative real-world task for any development platform.
Benchmark Results
The Horizon X3 CM posted compilation times of:
Run 1: 384.32 seconds (6 minutes 24 seconds)
Run 2: 376.66 seconds (6 minutes 17 seconds)
Run 3: 375.46 seconds (6 minutes 15 seconds)
Average: 378.81 seconds (6 minutes 19 seconds)
For context, here's how this compares to contemporary ARM and x86 single-board computers:
System
Architecture
CPU
Cores
Average Time
vs. X3 CM
Orange Pi 5 Max
ARM64
Cortex-A55/A76
8
62.31s
6.08x faster
Raspberry Pi CM5
ARM64
Cortex-A76
4
71.04s
5.33x faster
LattePanda Iota
x86_64
Intel N150
4
72.21s
5.25x faster
Raspberry Pi 5
ARM64
Cortex-A76
4
76.65s
4.94x faster
Horizon X3 CM
ARM64
Cortex-A53
4
378.81s
1.00x (baseline)
Orange Pi RV2
RISC-V
Ky X1
8
650.60s
1.72x slower
The Horizon X3 CM is approximately five times slower than the Raspberry Pi 5, despite both boards having four cores. This dramatic performance gap is explained by the generational difference in ARM core architecture: the Cortex-A76 in the Pi 5 represents eight years of microarchitectural advancement over the A53, with wider execution units, better branch prediction, higher clock speeds, and more sophisticated memory hierarchies.
The only platform slower than the X3 CM in our testing was the Orange Pi RV2, which uses an experimental RISC-V processor with an immature compiler toolchain. The fact that an established ARM platform with a mature software ecosystem performs only 1.72x better than a bleeding-edge RISC-V platform speaks volumes about the X3's inadequacy.
To complement our real-world compilation benchmarks, we also ran Geekbench 6 - an industry-standard synthetic benchmark that measures CPU performance across a variety of workloads including cryptography, image processing, machine learning, and general computation. The results reinforce and quantify just how far behind the Horizon X3 CM falls compared to modern alternatives.
For context, here's how this compares to other single-board computers running Geekbench 6:
System
CPU
Single-Core
Multi-Core
vs. X3 Single
vs. X3 Multi
Orange Pi 5 Max
Cortex-A55/A76
743
2,792
5.85x faster
7.37x faster
Raspberry Pi 5
Cortex-A76
764-774
1,588-1,604
6.01-6.09x faster
4.19-4.23x faster
Raspberry Pi 5 (OC)
Cortex-A76
837
1,711
6.59x faster
4.51x faster
Horizon X3 CM
Cortex-A53
127
379
1.00x (baseline)
1.00x (baseline)
The Geekbench results align remarkably well with our compilation benchmarks, confirming that the X3 CM's poor performance isn't specific to one workload but represents a fundamental computational deficit across all task types.
A single-core score of 127 is abysmal by 2025 standards. To put this in perspective, the iPhone 6s from 2015 scored around 140 in single-core Geekbench 6 tests. The Horizon X3 CM, released in 2021-2022, delivers performance comparable to a decade-old smartphone processor.
The multi-core score of 379 shows that the X3 fails to effectively leverage its four cores. Despite having the same core count as the Raspberry Pi 5, the X3 scores less than one-quarter of the Pi 5's multi-core performance. The Orange Pi 5 Max, with its eight cores (four A76 + four A55), absolutely destroys the X3 with 7.37x better multi-core performance.
The Geekbench individual test scores reveal specific weaknesses:
HTML5 Browser: 180 single-core (even web-based robot control interfaces would lag)
PDF Rendering: 200 single-core, 797 multi-core (document processing would crawl)
These synthetic benchmarks might seem academic, but they translate directly to real-world robotics performance. The navigation score predicts poor path planning performance. The Clang score explains the painful compilation times. The HTML5 browser score means even accessing web-based configuration interfaces will be sluggish. Every aspect of development and deployment on the X3 CM will feel slow because the processor is fundamentally inadequate.
What This Means for Real Workloads
The compilation benchmark translates directly to real-world robotics and AI development scenarios:
Development iteration time: Compiling ROS2 packages, building custom nodes, and testing changes takes five times longer than on a Raspberry Pi 5. A developer waiting 20 minutes for a build on the Pi 5 will wait 100 minutes on the X3 CM.
AI model training: While the BPU handles inference, any model training, data preprocessing, or optimization work runs on the Cortex-A53 cores at a glacial pace.
Computer vision processing: Pre-BPU image processing, post-BPU result processing, and any vision algorithms not optimized for the Bernoulli architecture will execute slowly.
Multi-tasking performance: Running ROS2, sensor drivers, motion controllers, and application logic simultaneously will strain the limited CPU resources. The cores will spend more time context switching than doing useful work.
The AI Promise: Hollow Marketing
Let's return to the central premise of the Horizon X3 CM: it's an AI-focused robotics platform with a dedicated Brain Processing Unit providing 5 TOPS of inference capability. Does this specialization justify the platform's shortcomings?
The answer is a resounding no.
First, 5 TOPS is not impressive by 2025 standards. The Google Coral TPU provides 4 TOPS in a USB dongle costing under $60. The NVIDIA Jetson Orin Nano provides 40 TOPS. Even smartphone SoCs like the Apple A17 Pro deliver over 35 TOPS. The Horizon X3's 5 TOPS might have been notable in 2020 when the chip was announced, but it's thoroughly uncompetitive five years later.
Second, the BPU's usefulness is limited by the proprietary toolchain and model conversion requirements. You can't simply take a TensorFlow or PyTorch model and run it on the BPU. It must be converted using Horizon's tools, quantized to specific formats the Bernoulli architecture supports, and optimized for the dual-core BPU's execution model. The documentation for this process is scattered, incomplete, and assumes familiarity with Horizon's automotive-focused development flow.
Third, the weak Cortex-A53 cores undermine any AI acceleration advantage. If your application spends 70% of its time in AI inference and 30% in CPU-bound tasks, accelerating the inference to near-zero still leaves you with performance dominated by the slow CPU. The system is only as fast as its slowest component, and the CPU is very slow.
Fourth, the ecosystem lock-in is severe. Code written for the Horizon BPU doesn't port to other platforms. Models optimized for Bernoulli architecture require re-optimization for other accelerators. Investing development time in Horizon-specific tooling is investing in a dead-end technology with an uncertain future.
Compare this to the Raspberry Pi ecosystem, where you can add AI acceleration through well-supported options like the Coral TPU, Intel Neural Compute Stick, or Hailo-8 accelerator. These solutions work across the Pi 4, Pi 5, and other platforms, with mature Python APIs, extensive documentation, and active communities. The development you do with these accelerators transfers to other projects and platforms.
Documentation: Scarce and Scattered
Throughout our evaluation of the Horizon X3 CM, a consistent theme emerged: finding documentation for any task ranged from difficult to impossible. Want to understand the BPU's capabilities? The information is spread across d-robotics.cc, developer.d-robotics.cc, archived Horizon Robotics pages, and forums in both English and Chinese.
Looking for example code? Some repositories on GitHub have examples, but they assume familiarity with Horizon's model conversion tools. The tools themselves have documentation, but it's automotive-focused and doesn't translate well to robotics applications.
Need help troubleshooting a problem? The forums are sparsely populated, with many questions unanswered. The most reliable source of information is reverse-engineering what other users have done and hoping it works on your hardware revision.
This stands in stark contrast to the Raspberry Pi ecosystem, where every sensor, every module, every software package has multiple tutorials, forums full of discussions, YouTube videos, and GitHub repositories with example code. The Pi's ubiquity means that any problem you encounter has likely been solved multiple times by others.
The YouTube Deception
It's worth addressing the several YouTube videos that demonstrate the Horizon X3 running robotics applications, performing object detection, and controlling robot platforms. These videos create an impression that the X3 is a viable robotics platform. They're not technically dishonest - the hardware can do these things - but they omit the critical context that makes the X3 a poor choice.
These demonstrations typically show:
Custom-built systems where someone has already overcome the configuration hurdles
Specific AI models that have been painstakingly optimized for the BPU
Applications that carefully avoid the CPU bottlenecks
No comparisons to how the same task performs on alternative platforms
No discussion of development time, tool chain difficulties, or ecosystem limitations
What they don't show is the hours spent fixing sudo, configuring networks, battling documentation gaps, and waiting for slow compilation. They don't mention that achieving the same functionality on a Raspberry Pi 5 with a Coral TPU would be faster to develop, more performant, better documented, and more maintainable.
The YouTube demonstrations are real, but they represent the absolute best case: experienced developers who've mastered the platform's quirks showing carefully crafted demos. They do not represent the typical user experience.
Who Is This For? (No One)
Attempting to identify the target audience for the Horizon X3 CM reveals its fundamental problem: there isn't a clear use case where it's the best choice.
Beginners: Absolutely not. The broken sudo, network configuration challenges, scattered documentation, and proprietary toolchain create insurmountable barriers for someone learning robotics development. A beginner choosing the X3 will spend 90% of their time fighting the platform and 10% actually learning robotics.
Intermediate developers: Still no. Someone with Linux experience and basic robotics knowledge will be frustrated by the X3's limitations. They have the skills to configure the system, but they'll quickly realize they're wasting time on a platform that's slower, less documented, and more restrictive than alternatives.
Advanced developers: Why would they choose this? An advanced developer evaluating SBC options will immediately recognize the Cortex-A53's limitations, the proprietary BPU lock-in, and the ecosystem fragmentation. They'll choose a Raspberry Pi with modular acceleration, or an NVIDIA Jetson if they need serious AI performance, or an x86 platform if they need raw CPU power.
Automotive developers: This is Horizon's actual target market, but they're not using the off-the-shelf RDK X3 boards. They're integrating the Sunrise chips into custom hardware with proprietary board support packages, automotive-grade Linux distributions, and Horizon's professional support contracts.
The hobbyist robotics market that the RDK X3 ostensibly targets is better served by literally any other option. The Raspberry Pi ecosystem offers superior hardware, vastly better documentation, more active communities, and modular expandability. Even the aging Raspberry Pi 4 is arguably a better choice than the X3 CM for most robotics projects.
Conclusion: An Irrelevant Platform in 2025
The Horizon X3 CM represents a failed experiment in bringing automotive AI technology to the robotics hobbyist market. The hardware is built on outdated ARM cores that were unimpressive when they launched in 2012 and are thoroughly inadequate in 2025. The AI acceleration, while technically present, is hamstrung by weak CPUs, proprietary tooling, and an abandoned software ecosystem. The software distributions ship broken, requiring extensive manual fixes to achieve basic functionality.
Our performance testing confirms what the specifications suggest: the X3 CM is approximately five times slower than a current-generation Raspberry Pi 5 for CPU-bound workloads. Both our real-world Rust compilation benchmarks and industry-standard Geekbench 6 synthetic tests show consistent results - the X3 CM delivers single-core performance 6x slower and multi-core performance 4-7x slower than modern competition. The BPU's 5 TOPS of AI acceleration cannot compensate for this massive performance deficit, and the proprietary nature of the Bernoulli architecture creates vendor lock-in without providing compelling advantages.
The documentation situation is dire, with information scattered across multiple sites in multiple languages, many links pointing to archived or defunct resources. The corporate structure - Horizon Robotics abandoning public development while D-Robotics maintains forks - raises serious questions about long-term support and viability.
For anyone considering robotics development in 2025, the recommendation is clear: avoid the Horizon X3 CM. If you're a beginner, start with a Raspberry Pi 5 - you'll have vastly more resources available, a supportive community, and hardware that won't frustrate you at every turn. If you're an intermediate or advanced developer, the Pi 5 with optional AI acceleration (Coral TPU, Hailo-8) will give you more flexibility, better performance, and a lower total cost of ownership. If you need serious AI horsepower, look at NVIDIA's Jetson line, which provides professional-grade AI acceleration with mature tooling and extensive documentation.
The Horizon X3 CM is a platform that perhaps made sense when announced in 2020-2021, competing against the Raspberry Pi 4 and targeting a market that was just beginning to explore edge AI. But time has not been kind. The ARM cores have aged poorly, the software ecosystem never achieved critical mass, and the corporate support has evaporated. In 2025, choosing the Horizon X3 CM for a new robotics project is choosing to fight your tools rather than build your robot.
The most damning evidence is this: even the Orange Pi RV2, running a brand-new RISC-V processor with an immature compiler toolchain and experimental software stack, is only 1.72x slower than the X3 CM. An experimental architecture with bleeding-edge hardware and alpha-quality software performs almost as well as an established ARM platform with supposedly mature tooling. Both our real-world compilation benchmarks and Geekbench 6 synthetic tests confirm the X3 CM's performance is comparable to a decade-old iPhone 6s processor - a smartphone chip from 2015 outperforms this 2021-2022 era robotics development platform. This speaks volumes about just how underpowered and poorly optimized the Horizon X3 CM truly is.
Save yourself the frustration. Build your robot on a platform that respects your time, provides the tools you need, and has a future. The Raspberry Pi ecosystem is the obvious choice, but almost any alternative - even commodity x86 mini-PCs - would serve you better than the Horizon X3 CM.
Specifications Summary
For reference, here are the complete specifications of the Horizon X3 CM:
Processor:
Sunrise X3 SoC (16nm process)
Quad-core ARM Cortex-A53 @ 1.5 GHz
Single ARM Cortex-R5 core
Dual-core Bernoulli 2.0 BPU (5 TOPS AI inference)
Memory & Storage:
2GB or 4GB LPDDR4 RAM
8GB/16GB/32GB eMMC options
MicroSD card slot
Video:
4K@60fps H.264/H.265 encoding
4K@60fps decoding
HDMI 2.0 output
Interfaces:
2x MIPI CSI (camera input)
1x MIPI DSI (display output)
2x USB 3.0
Gigabit Ethernet
40-pin GPIO header
I2C, SPI, UART, PWM
Physical:
200-pin board-to-board connector (CM4-compatible)
Dimensions: 55mm x 40mm
Software:
Ubuntu 20.04/22.04 based distributions
ROS2 support (in theory)
Horizon OpenExplorer development tools
Benchmark Performance:
Rust compilation: 378.81 seconds average (5x slower than Raspberry Pi 5)
Geekbench 6 Single-Core: 127 (6x slower than Raspberry Pi 5)
Geekbench 6 Multi-Core: 379 (4-7x slower than modern ARM SBCs)
This report compares the inference performance of two GPU systems running local LLM models using Ollama. The benchmark tests were conducted using the llm-tester tool with concurrent requests set to 1, simulating single-user workload scenarios.
AMD RX 7900 XTX performance on deepseek-r1:1.5b model
Max+ 395 performance on deepseek-r1:1.5b model
qwen3:latest Performance
System
Avg Tokens/s
Avg Latency
Total Time
Performance Ratio
AMD RX 7900
86.46
12.81s
64.04s
2.71x faster
Max+ 395
31.85
41.00s
204.98s
baseline
Detailed Results - AMD RX 7900:
Task 1: 86.56 tokens/s, Latency: 15.07s
Task 2: 85.69 tokens/s, Latency: 18.37s
Task 3: 86.74 tokens/s, Latency: 7.15s
Task 4: 87.91 tokens/s, Latency: 1.56s
Task 5: 85.43 tokens/s, Latency: 21.90s
Detailed Results - Max+ 395:
Task 1: 32.21 tokens/s, Latency: 33.15s
Task 2: 27.53 tokens/s, Latency: 104.82s
Task 3: 33.47 tokens/s, Latency: 16.79s
Task 4: 34.96 tokens/s, Latency: 4.64s
Task 5: 31.08 tokens/s, Latency: 45.59s
AMD RX 7900 XTX performance on qwen3:latest model
Max+ 395 performance on qwen3:latest model
Comparative Analysis
Overall Performance Summary
Model
RX 7900
Max+ 395
Performance Multiplier
deepseek-r1:1.5b
197.01 tok/s
110.52 tok/s
1.78x
qwen3:latest
86.46 tok/s
31.85 tok/s
2.71x
Key Findings
RX 7900 Dominance: The AMD RX 7900 significantly outperforms the Max+ 395 across both models
78% faster on deepseek-r1:1.5b
171% faster on qwen3:latest
Model-Dependent Performance Gap: The performance difference is more pronounced with the larger/more complex model (qwen3:latest), suggesting the RX 7900 handles larger models more efficiently
Consistency: The RX 7900 shows more consistent performance across tasks, with lower variance in latency
Total Execution Time:
For deepseek-r1:1.5b: RX 7900 completed in 32.72s vs 107.53s (3.3x faster)
For qwen3:latest: RX 7900 completed in 64.04s vs 204.98s (3.2x faster)
AMD RX 7900: Available as standalone GPU (~$600-800 used, ~$900-1000 new)
Value Proposition
The AMD RX 7900 delivers:
1.78-2.71x better performance than the Max+ 395
Significantly better price-to-performance ratio (~$800 vs $2,500)
Dedicated GPU VRAM vs shared unified memory
Better thermal management in desktop form factor
The $2,500 Framework Desktop investment could alternatively fund:
AMD RX 7900 GPU
High-performance desktop motherboard
AMD Ryzen CPU
32-64GB DDR5 RAM
Storage and cooling
With budget remaining
Conclusions
Clear Performance Winner: The AMD RX 7900 is substantially faster than the Max+ 395 for LLM inference workloads
Value Analysis: The Framework Desktop's $2,500 price point doesn't provide competitive performance for LLM workloads compared to desktop alternatives
Use Case Consideration: The Framework Desktop offers portability and unified memory benefits, but if LLM performance is the primary concern, the RX 7900 desktop configuration is superior
ROCm Compatibility: Both systems successfully ran ROCm workloads, demonstrating AMD's growing ecosystem for AI/ML tasks
Recommendation: For users prioritizing LLM inference performance per dollar, a desktop workstation with an RX 7900 provides significantly better value than the Max+ 395 Framework Desktop
Technical Notes
All tests used identical benchmark methodology with single concurrent requests
Both systems were running similar ROCm configurations
Machine learning on AMD GPUs has always been... interesting. With NVIDIA's CUDA dominating the landscape, AMD's ROCm platform remains the underdog—powerful, but often requiring patience and persistence to get working properly. This is the story of how I got YOLOv8 object detection training working on an AMD Radeon 8060S integrated GPU (gfx1151) in the AMD RYZEN AI MAX+ 395 after encountering batch normalization failures, version mismatches, and a critical bug in MIOpen.
The goal was simple: train a bullet hole detection model for a ballistics application using YOLOv8. The journey? Anything but simple.
MIOpen: Initially 3.0.5.1 (version code 3005001), later custom build
OS: Linux (conda environment: pt2.8-rocm7)
The AMD Radeon 8060S is an integrated GPU in the AMD RYZEN AI MAX+ 395 based on AMD's RDNA 3.5 architecture (gfx1151). What makes this system particularly interesting for machine learning is the massive 96GB of shared system memory available to the GPU—far more VRAM than typical consumer discrete GPUs. While machine learning support on RDNA 3.5 is still maturing compared to older RDNA 2 architectures, the memory capacity makes it compelling for AI workloads.
Before diving into the technical challenges, it's worth explaining why we chose YOLOv8 from Ultralytics for this project.
YOLOv8 (You Only Look Once, version 8) is the latest iteration of one of the most popular object detection architectures. Developed and maintained by Ultralytics, it offers several advantages:
Why Ultralytics YOLOv8?
State-of-the-art Accuracy: YOLOv8 achieves excellent detection accuracy while maintaining real-time inference speeds—critical for practical applications.
Ease of Use: Ultralytics provides a clean, well-documented Python API that makes training custom models remarkably straightforward:
Active Development: Ultralytics is actively maintained with frequent updates, bug fixes, and community support. This proved invaluable during debugging.
Model Variants: YOLOv8 comes in multiple sizes (nano, small, medium, large, extra-large), allowing us to balance accuracy vs. speed for our specific use case.
Built-in Data Augmentation: The framework includes extensive data augmentation capabilities out of the box—essential for training robust detection models with limited training data.
PyTorch Native: Being built on PyTorch meant it should theoretically work with ROCm (AMD's CUDA equivalent)... in theory.
For our bullet hole detection application, YOLOv8's ability to accurately detect small objects (bullet holes in paper targets) while training efficiently made it the obvious choice. Little did I know that "training efficiently" would require a week-long debugging odyssey.
The Initial Setup (ROCm 7.0.0)
I started with ROCm 7.0.0, following AMD's official installation guide. Everything installed cleanly:
The error was cryptic, but digging deeper revealed the real issue—MIOpen was failing to compile batch normalization kernels with inline assembly errors:
<inline asm>:14:20: error: not a valid operand.
v_add_f32 v4 v4 v4 row_bcast:15 row_mask:0xa
^
Batch normalization. The most common operation in modern deep learning, and it was failing spectacularly on gfx1151. The inline assembly instructions (row_bcast and row_mask) appeared incompatible with the RDNA 3.5 architecture.
What is Batch Normalization?
Batch normalization (BatchNorm) is a technique that normalizes layer inputs across a mini-batch, helping neural networks train faster and more stably. It's used in virtually every modern CNN architecture, including YOLO.
The error message pointed to MIOpen, AMD's equivalent of NVIDIA's cuDNN—a library of optimized deep learning primitives.
Attempt 1: Upgrade to ROCm 7.0.2
My first instinct was to upgrade ROCm. Version 7.0.0 was relatively new, and perhaps 7.0.2 had fixed the batch normalization issues.
# Upgraded PyTorch to ROCm 7.0.2
pipinstall--upgradetorch--index-urlhttps://download.pytorch.org/whl/rocm7.0
Result? Same error. Batch normalization still failed.
RuntimeError:miopenStatusUnknownError
With the same inline assembly compilation errors about invalid row_bcast and row_mask operands. At this point, I realized this wasn't a simple version mismatch—there was something fundamentally broken with MIOpen's batch normalization implementation for the gfx1151 architecture.
The Revelation: It's MIOpen, Not ROCm
After hours of testing different PyTorch versions, driver configurations, and kernel parameters, I turned to the ROCm community for help.
I posted my issue on Reddit's r/ROCm subreddit, describing the inline assembly compilation failures and miopenStatusUnknownError on gfx1151. Within a few hours, a knowledgeable Redditor responded with a crucial piece of information:
"There's a known issue with MIOpen 3.0.x and gfx1151 batch normalization. The inline assembly instructions use operands that aren't compatible with RDNA 3. A fix was recently merged into the develop branch. Try using a nightly build of MIOpen or build from source."
This was the breakthrough I needed. The issue wasn't with ROCm itself or PyTorch—it was specifically MIOpen version 3.0.5.1 that shipped with ROCm 7.0.x. The maintainers had already fixed the gfx1151 batch normalization bug in a recent pull request, but it hadn't made it into a stable release yet.
The Reddit user suggested two options:
Use a nightly Docker container with the latest MIOpen build
Build MIOpen 3.5.1 from source using the develop branch
Testing the Theory: Docker Nightly Builds
Before committing to building from source, I wanted to verify that a newer MIOpen would actually fix the problem. AMD provides nightly Docker images with bleeding-edge ROCm builds:
It worked! The miopenStatusUnknownError was gone, no more inline assembly compilation failures. Training completed successfully with MIOpen 3.5.1 from the develop branch. The newer version had updated the batch normalization kernels to use instructions compatible with RDNA 3.5's gfx1151 architecture.
This confirmed the Reddit user's tip: the fix was indeed in the newer MIOpen code that hadn't been released in a stable version yet.
The Solution: Building MIOpen from Source
Docker was great for testing, but I needed a permanent solution for my native conda environment. That meant building MIOpen 3.5.1 from source.
Step 1: Clone the Repository
cd~/ballistics_training
gitclonehttps://github.com/ROCm/MIOpen.gitrocm-libraries/projects/miopen
cdrocm-libraries/projects/miopen
gitcheckoutdevelop# Latest development branch with gfx1151 fixes
It worked! Batch normalization executed flawlessly. The training progressed smoothly from epoch to epoch, with GPU utilization staying high, memory management remaining stable, and losses converging as expected. The model achieved 53.0% mAP50 and trained without a single error.
After a week of debugging, version wrangling, and source code compilation, I finally had GPU-accelerated YOLOv8 training working on my AMD RDNA 3.5 GPU. The custom MIOpen 3.5.1 build resolved the inline assembly compatibility issues, and training now runs as smoothly on gfx1151 as it would on any other supported GPU.
Performance Notes
With the custom MIOpen build, training performance was excellent:
Training Speed: 70.5 images/second (batch size 16, 416×416 images)
Training Time: 32.6 seconds for 10 epochs (2,300 total images)
Throughput: 9.7-9.9 iterations/second
GPU Utilization: ~95% during training with no throttling
Memory Usage: ~1.2 GB VRAM for YOLOv8n with batch size 16
The GPU utilization stayed consistently high with no performance degradation across epochs. Each epoch averaged approximately 3.3 seconds with solid consistency. For comparison, CPU-only training on the same dataset would be roughly 15-20x slower. The GPU acceleration was well worth the effort.
Lessons Learned
This debugging journey taught me several valuable lessons:
1. The ROCm Community is Invaluable
The Reddit r/ROCm community proved to be the key to solving this issue. When official documentation fails, community knowledge fills the gap. Don't hesitate to ask for help—chances are someone has encountered your exact issue before.
2. MIOpen ≠ ROCm
I initially assumed upgrading ROCm would fix the problem. In reality, MIOpen (the deep learning library) had a separate bug that was independent of the ROCm platform version. Understanding the component architecture of ROCm saved hours of debugging time.
3. RDNA 3.5 (gfx1151) Support is Still Maturing
AMD's latest integrated GPU architecture is powerful, but ML support lags behind older architectures like RDNA 2 (gfx1030) and Vega. If you're doing serious ML work on AMD, consider that newer hardware may require more troubleshooting.
4. Nightly Builds Can Be Production-Ready
There's often hesitation to use nightly/development builds in production. However, in this case, the develop branch of MIOpen was actually more stable than the official release for my specific GPU. Sometimes bleeding-edge code is exactly what you need.
5. Docker is Great for Testing
The ROCm nightly Docker containers were instrumental in proving my hypothesis. Being able to test a newer MIOpen version without committing to a full rebuild saved significant time.
6. Source Builds Give You Control
Building from source is time-consuming and requires understanding the build system, but it gives you complete control over your environment. When binary distributions fail, source builds are your safety net.
Tips for AMD GPU Machine Learning
If you're attempting to do machine learning on AMD GPUs, here are some recommendations:
Environment Setup
Use conda/virtualenv: Isolate your Python environment to avoid system package conflicts
Pin your versions: Lock PyTorch, ROCm, and MIOpen versions once you have a working setup
Keep backups: Always backup working library files before swapping them out
Test simple operations: Try basic tensor operations before complex models
Check MIOpen version: torch.backends.cudnn.version() can reveal version mismatches
Monitor logs: ROCm logs (MIOPEN_ENABLE_LOGGING=1) provide valuable debugging info
Try Docker first: Test potential fixes in Docker before modifying your system
Hardware Considerations
RDNA 2 (gfx1030) is more mature than RDNA 3.5 (gfx1151) for ML workloads
Server GPUs (MI series) have better ROCm support than consumer cards
Integrated GPUs with large shared memory (like the Radeon 8060S with 96GB) offer unique advantages for ML
Check compatibility: Always verify your specific GPU (gfx code) is supported before purchasing
Conclusion
Getting YOLOv8 training working on an AMD RDNA 3.5 GPU wasn't easy, but it was achievable. The combination of:
Community support from r/ROCm pointing me to the right solution
Docker testing to verify the fix
Building MIOpen 3.5.1 from source
Carefully replacing system libraries
...resulted in a fully functional GPU-accelerated machine learning training environment.
AMD's ROCm platform still has rough edges compared to NVIDIA's CUDA ecosystem, but it's improving rapidly. With some patience, persistence, and willingness to dig into source code, AMD GPUs can absolutely be viable for machine learning workloads.
The bullet hole detection model trained successfully, achieved excellent accuracy, and now runs in production. Sometimes the journey is as valuable as the destination—I learned more about ROCm internals, library dependencies, and GPU computing in this week than I would have in months of smooth sailing.
If you're facing similar issues with AMD GPUs and ROCm, I hope this guide helps. And remember: when in doubt, check r/ROCm. The community might just have the answer you're looking for.
The Orange Pi RV2: Cost-effective 8-core RISC-V development board
When the Orange Pi RV2 arrived for testing, it represented something fundamentally different from the dozens of ARM and x86 single board computers that have crossed my desk over the years. This wasn't just another Cortex-A76 board with slightly tweaked specifications or a new Intel Atom variant promising better performance-per-watt. The Orange Pi RV2, powered by the Ky(R) X1 processor, represents one of the first commercially available RISC-V single board computers aimed at the hobbyist and developer market. It's a glimpse into a future where processor architecture diversity might finally break the ARM-x86 duopoly that has dominated single board computing as of late.
But is RISC-V ready for prime time? Can it compete with the mature ARM ecosystem that powers everything from smartphones to supercomputers, or the x86 architecture that has dominated desktop and server computing for over four decades? I put the Orange Pi RV2 through the same rigorous benchmarking suite I use for all single board computers, comparing it directly against established platforms including the Raspberry Pi 5, Raspberry Pi Compute Module 5, Orange Pi 5 Max, and LattePanda IOTA. The results tell a fascinating story about where RISC-V stands today and where it might be heading.
What is RISC-V and Why Does it Matter?
Before diving into performance numbers, it's worth understanding what makes RISC-V different. Unlike ARM or x86, RISC-V is an open instruction set architecture. This means anyone can implement RISC-V processors without paying licensing fees or negotiating complex agreements with chip vendors. The specification is maintained by RISC-V International, a non-profit organization, and the core ISA is frozen and will never change.
This openness has led to an explosion of academic research and commercial implementations. Companies like SiFive, Alibaba, and now apparently Ky have developed RISC-V cores targeting everything from embedded microcontrollers to high-performance application processors. The promise is compelling: a truly open architecture that could democratize processor design and break vendor lock-in.
However, openness alone doesn't guarantee performance or ecosystem maturity. The RISC-V software ecosystem is still catching up to ARM and x86, with toolchains, operating systems, and applications at various stages of optimization. The Orange Pi RV2 gives us a real-world test of where this ecosystem stands in late 2024 and early 2025.
The Orange Pi RV2: Specifications and Setup
Top view showing the Ky X1 RISC-V processor and 8GB RAM
The Orange Pi RV2 features the Ky(R) X1 processor, an 8-core RISC-V chip running at up to 1.6 GHz. The system ships with Orange Pi's custom Linux distribution based on Ubuntu Noble, running kernel 6.6.63-ky. The board includes 8GB of RAM, sufficient for most development tasks and light server workloads.
Side view showing USB 3.0 ports, Gigabit Ethernet, and HDMI connectivity
Setting up the Orange Pi RV2 proved straightforward. The board boots from SD card and includes SSH access out of the box. Installing Rust, the language I use for compilation benchmarks, required building from source rather than using rustup, as RISC-V support in rustup is still evolving. Once installed, I had rustc 1.90.0 and cargo 1.90.0 running successfully.
The system presents itself as:
Linux orangepirv2 6.6.63-ky #1.0.0 SMP PREEMPT Wed Mar 12 09:04:00 CST 2025 riscv64 riscv64 riscv64 GNU/Linux
One immediate observation: this kernel was compiled in March 2025, suggesting very recent development. This is typical of the RISC-V SBC space right now - these boards are so new that kernel and userspace support is being actively developed, sometimes just weeks or months before the hardware ships.
Bottom view showing eMMC connector and M.2 key expansion
The Competition: ARM64 and x86_64 Platforms
To properly evaluate the Orange Pi RV2, I compared it against four other single board computers representing the current state of ARM and x86 in this form factor.
The Raspberry Pi 5 and Raspberry Pi Compute Module 5 both feature the Broadcom BCM2712 with four Cortex-A76 cores running at 2.4 GHz. These represent the current flagship for the Raspberry Pi Foundation, widely regarded as the gold standard for hobbyist and education-focused SBCs. The standard Pi 5 averaged 76.65 seconds in compilation benchmarks, while the CM5 came in slightly faster, demonstrating the maturity of ARM's Cortex-A76 architecture.
The Orange Pi 5 Max takes a different approach with its Rockchip RK3588 SoC, featuring a big.LITTLE configuration with four Cortex-A76 cores and four Cortex-A55 efficiency cores, totaling eight cores. This heterogeneous architecture allows the system to balance performance and power consumption. In my testing, the Orange Pi 5 Max posted the fastest compilation times among the ARM platforms, leveraging all eight cores effectively.
On the x86 side, the LattePanda IOTA features Intel's N150 processor, a quad-core Alder Lake-N chip. This represents Intel's current low-power x86 offering, designed to compete directly with ARM in the SBC and mini-PC market. The N150 delivered solid performance with an average compilation time of 72.21 seconds, demonstrating that x86 can still compete in this space when properly optimized.
Compilation Performance: The Rust Test
Comprehensive compilation performance comparison across all platforms
My primary benchmark involves compiling a Rust project - specifically, a ballistics engine with significant computational complexity and numerous dependencies. This real-world workload stresses the CPU, memory subsystem, and compiler toolchain in ways that synthetic benchmarks often miss. I perform three clean compilation runs on each system and average the results.
The results were striking:
Orange Pi 5 Max (ARM64, RK3588, 8 cores): 62.35 seconds average
LattePanda IOTA (x86_64, Intel N150, 4 cores): 72.21 seconds average
Raspberry Pi 5 (ARM64, BCM2712, 4 cores): 76.65 seconds average
Raspberry Pi CM5 (ARM64, BCM2712, 4 cores): ~74 seconds average
Orange Pi RV2 (RISC-V, Ky X1, 8 cores): 650.60 seconds average
The Orange Pi RV2's compilation times of 661.25, 647.39, and 643.16 seconds averaged out to 650.60 seconds - more than ten times slower than the Orange Pi 5 Max and nearly nine times slower than the Raspberry Pi 5. Despite having eight cores compared to the Pi 5's four, the RISC-V platform lagged dramatically behind.
This performance gap isn't simply about clock speeds or core counts. The Orange Pi RV2 runs at 1.6 GHz compared to the Pi 5's 2.4 GHz, but that 1.5x difference in frequency doesn't explain a 10x difference in compilation time. Instead, we're seeing the combined effect of several factors:
Processor microarchitecture maturity - ARM's Cortex-A76 represents over a decade of iterative improvement, while the Ky X1 is a first-generation design
Compiler optimization - LLVM's ARM backend has been optimized for years, while RISC-V support is much newer
Memory subsystem performance - the Ky X1's memory controller and cache hierarchy appear significantly less optimized
Single-threaded performance - compilation is often limited by single-threaded tasks, where the ARM cores have a significant advantage
It's worth noting that the Orange Pi RV2 showed good consistency across runs, with only about 2.8 percent variation between the fastest and slowest compilation. This suggests the hardware itself is stable; it's simply not competitive with current ARM or x86 offerings for this workload.
The Ecosystem Challenge: Toolchains and Software
Beyond raw performance, the RISC-V ecosystem faces significant maturity challenges. This became evident when attempting to run llama.cpp, the popular framework for running large language models locally. Following Jeff Geerling's guide for building llama.cpp on RISC-V, I immediately hit toolchain issues.
The llama.cpp build system detected RISC-V vector extensions and attempted to compile with -march=rv64gc_zfh_v_zvfh_zicbop, enabling hardware support for floating-point operations and vector processing. However, the GCC 13.3.0 compiler shipping with Orange Pi's Linux distribution didn't fully support these extensions, producing errors about unexpected ISA strings.
The workaround was to disable RISC-V vector support entirely:
By compiling with basic rv64gc instructions only - essentially the baseline RISC-V instruction set without advanced SIMD capabilities - the build succeeded. But this immediately highlights a key ecosystem problem: the mismatch between hardware capabilities, compiler support, and software assumptions.
On ARM or x86 platforms, these issues were solved years ago. When you compile llama.cpp on a Raspberry Pi 5, it automatically detects and uses NEON SIMD instructions. On x86, it leverages AVX2 or AVX-512 if available. The toolchain, runtime detection, and fallback mechanisms all work seamlessly because they've been tested and refined over countless deployments.
RISC-V is still working through these growing pains. The vector extensions exist in the specification and are implemented in hardware on some processors, but compiler support varies, software doesn't always detect capabilities correctly, and fallback paths aren't always reliable. This forced me to compile llama.cpp in its least optimized mode, guaranteeing compatibility but leaving significant performance on the table.
Running LLMs on RISC-V: TinyLlama Performance
Despite the toolchain challenges, I successfully built llama.cpp and downloaded TinyLlama 1.1B in Q4_K_M quantization - a relatively small language model suitable for testing on resource-constrained devices. Running inference revealed exactly what you'd expect given the compilation benchmarks: functional but slow performance.
Prompt processing achieved 0.87 tokens per second, taking 1,148 milliseconds per token to encode the input. Token generation during the actual response was even slower at 0.44 tokens per second, or 2,250 milliseconds per token. To generate a 49-token response to "What is RISC-V?" took 110 seconds total.
For context, the same TinyLlama model on a Raspberry Pi 5 typically achieves 5-8 tokens per second, while the LattePanda IOTA manages 8-12 tokens per second depending on quantization. High-end ARM boards like the Orange Pi 5 Max can exceed 15 tokens per second with this model. The Orange Pi RV2's 0.44 tokens per second puts it roughly 11-34x slower than comparable ARM and x86 platforms.
The LLM did produce correct output, successfully explaining RISC-V as "a software-defined architecture for embedded and real-time systems" before noting it was "open-source and community-driven." The accuracy of the output confirms that the RISC-V platform is functionally correct - it's running the same model with the same weights and producing equivalent results. But the performance makes interactive use impractical for anything beyond basic testing and development.
What makes this particularly interesting is that we disabled vector instructions entirely. On ARM and x86 platforms, SIMD instructions provide massive speedups for the matrix multiplications that dominate LLM inference. The Orange Pi RV2 theoretically has vector extensions that could provide similar acceleration, but the immature toolchain forced us to leave them disabled. When RISC-V compiler support matures and llama.cpp can reliably use these hardware capabilities, we might see 2-4x performance improvements - though that would still leave RISC-V trailing ARM significantly.
The State of RISC-V SBCs: Pioneering Territory
It's important to contextualize these results within the broader RISC-V SBC landscape. These boards are extraordinarily new to the market. While ARM-based SBCs have evolved over 12+ years since the original Raspberry Pi, and x86 SBCs have existed even longer, RISC-V platforms aimed at developers and hobbyists have only emerged in the past two years.
The Orange Pi RV2 is essentially a first-generation product in a first-generation market. For comparison, the original Raspberry Pi from 2012 featured a single-core ARM11 processor running at 700 MHz and struggled with basic desktop tasks. Nobody expected it to compete with contemporary x86 systems; it was revolutionary simply for existing at a $35 price point and running Linux.
RISC-V is in a similar position today. The existence of an eight-core RISC-V SBC that can boot Ubuntu, compile complex software, and run large language models is itself remarkable. Five years ago, RISC-V was primarily found in microcontrollers and academic research chips. The progress to application-class processors running general-purpose operating systems has been rapid.
The ecosystem is growing faster than most observers expected. Major distributions like Debian, Fedora, and Ubuntu now provide official RISC-V images. The Rust programming language has first-class RISC-V support in its compiler. Projects like llama.cpp, even with their current limitations, are actively working on RISC-V optimization. Hardware vendors beyond SiFive and Chinese manufacturers are beginning to show interest, with Qualcomm and others investigating RISC-V for specific use cases.
What we're seeing with the Orange Pi RV2 isn't a mature product competing with established platforms - it's a pioneer platform demonstrating what's possible and revealing where work remains. The 10x performance gap versus ARM isn't a fundamental limitation of the RISC-V architecture; it's a measure of how much optimization work ARM has received over the past decade that RISC-V hasn't yet enjoyed.
Where RISC-V Goes From Here
The question isn't whether RISC-V will improve, but how quickly and how much. Several factors suggest significant progress in the near term:
Compiler maturity will improve rapidly as RISC-V gains adoption. LLVM and GCC developers are actively optimizing RISC-V backends, and major software projects are adding RISC-V-specific optimizations. The vector extension issues I encountered will be resolved as compilers catch up with hardware capabilities.
Processor implementations will evolve quickly. The Ky X1 in the Orange Pi RV2 is an early design, but Chinese semiconductor companies are investing heavily in RISC-V, and Western companies are beginning to follow. Second and third-generation designs will benefit from lessons learned in these first products.
Software ecosystem development is accelerating. Critical applications are being ported and optimized for RISC-V, from machine learning frameworks to databases to web servers. As this software matures, RISC-V systems will become more practical for real workloads.
The standardization of extensions will help. RISC-V's modular approach allows vendors to pick and choose which extensions to implement, but this creates fragmentation. As the ecosystem consolidates around standard profiles - baseline feature sets that software can depend on - compatibility and optimization will improve.
However, RISC-V faces challenges that ARM and x86 don't. The lack of a dominant vendor means fragmentation is always a risk. The openness that makes RISC-V attractive also means there's no single company with ARM or Intel's resources pushing the architecture forward. Progress depends on collective ecosystem development rather than centralized decision-making.
For hobbyists and developers today, RISC-V boards like the Orange Pi RV2 serve a specific purpose: experimentation, learning, and contributing to ecosystem development. If you want the fastest compilation times, most compatible software, or best performance per dollar, ARM or x86 remain superior choices. But if you want to be part of an emerging architecture, contribute to open-source development, or simply understand an alternative approach to processor design, RISC-V offers unique opportunities.
Conclusion: A Promising Start
The Orange Pi RV2 demonstrates both the promise and the current limitations of RISC-V in the single board computer space. It's a functional, stable platform that successfully runs complex workloads - just not quickly compared to established alternatives. The 650-second compilation times and 0.44 tokens-per-second LLM inference are roughly 10x slower than comparable ARM platforms, but they work correctly and consistently.
This performance gap isn't surprising or condemning. It reflects where RISC-V is in its maturity curve: early, promising, but not yet optimized. The architecture itself has no fundamental limitations preventing it from reaching ARM or x86 performance levels. What's missing is time, optimization work, and ecosystem development.
For anyone considering the Orange Pi RV2 or similar RISC-V boards, set expectations appropriately. This isn't a Raspberry Pi 5 competitor in raw performance. It's a development platform for exploring a new architecture, contributing to open-source projects, and learning about processor design. If those goals align with your interests, the Orange Pi RV2 is a fascinating platform. If you need maximum performance for compilation, machine learning, or general computing, stick with ARM or x86 for now.
But watch this space. RISC-V is moving faster than most expected, and platforms like the Orange Pi RV2 are pushing the boundaries of what's possible with open processor architectures. The 10x performance gap today might be 3x in two years and negligible in five. We're witnessing the early days of a potential revolution in processor architecture, and being able to participate in that development is worth more than a few minutes of faster compile times.
The future of computing might not be exclusively ARM or x86. If RISC-V continues its current trajectory, we could see a genuinely competitive third architecture in the mainstream within this decade. The Orange Pi RV2 is an early step on that journey - imperfect, slow by current standards, but undeniably significant.
Disclosure: DFRobot provided the LattePanda IOTA for this review. All other boards (Raspberry Pi 5, Raspberry Pi CM5, and Orange Pi 5 Max) were purchased with my own funds. All testing was conducted independently, and opinions expressed are my own.
Introduction: A New Challenger Enters the SBC Arena
The single board computer market has been dominated by ARM-based solutions for years, with Raspberry Pi leading the charge and alternatives like Orange Pi offering compelling price-to-performance ratios. When DFRobot sent me their LattePanda IOTA for testing, I was immediately intrigued by a fundamental question: how does Intel's latest low-power x86_64 architecture stack up against the best ARM SBCs available today?
The LattePanda IOTA represents something different in the SBC space. Built around Intel's N150 processor, it brings x86_64 compatibility to a form factor and price point traditionally dominated by ARM chips. This means native compatibility with the vast ecosystem of x86 software, development tools, and operating systems—no emulation or translation layers required.
To put the IOTA through its paces, I assembled a formidable lineup of competitors: the Raspberry Pi 5, Raspberry Pi CM5 (Compute Module 5), and the Orange Pi 5 Max. Each of these boards represents the cutting edge of ARM-based SBC design, making them ideal benchmarks for evaluating the IOTA's capabilities.
The Test Bench: Four Titans of the SBC World
LattePanda IOTA - The x86_64 Contender
The LattePanda IOTA booting up - x86 performance in a compact form factor
The LattePanda IOTA is DFRobot's answer to the question: "What if we brought modern x86 performance to the SBC world?" Built on Intel's N150 processor (Alder Lake-N architecture), it's a quad-core chip designed for efficiency and performance in compact devices.
Specifications:
CPU: Intel N150 (4 cores, up to 3.6 GHz)
Architecture: x86_64
TDP: 6W design
Memory: Supports up to 16GB LPDDR5
Connectivity: Wi-Fi 6, Bluetooth 5.2, Gigabit Ethernet
Storage: M.2 NVMe SSD support, eMMC options
I/O: USB 3.2, USB-C with DisplayPort Alt Mode, HDMI 2.0
The LattePanda IOTA with PoE expansion board - compact yet feature-rich
Unique Features:
Native x86 compatibility: Run any x86_64 Linux distribution, Windows 10/11, or even ESXi without compatibility concerns
M.2 NVMe support: Unlike many ARM SBCs, the IOTA supports high-speed NVMe storage out of the box
USB-C DisplayPort Alt Mode: Single-cable 4K display output and power delivery
RP2040 co-processor: Built-in RP2040 microcontroller (same chip as Raspberry Pi Pico) for hardware interfacing and GPIO operations
Dual display support: HDMI 2.0 and USB-C DP for multi-monitor setups
Pre-installed heatsink: Comes with proper thermal management from the factory
Close-up showing the RP2040 co-processor, PoE module, and connectivity options
The IOTA's party trick is its RP2040 co-processor—the same dual-core ARM Cortex-M0+ microcontroller found in the Raspberry Pi Pico. While the main Intel CPU handles compute-intensive tasks, the RP2040 manages GPIO, sensors, and hardware interfacing—essentially giving you two computers in one. This is particularly valuable for robotics, home automation, and IoT projects where you need both computational power and reliable real-time hardware control.
For Arduino IDE compatibility, newer versions support the RP2040 directly using the standard Raspberry Pi Pico board configuration. However, if you're using older versions of the Arduino IDE, you can take advantage of the microcontroller by selecting the LattePanda Leonardo board option, which provides compatibility with the IOTA's hardware configuration.
Raspberry Pi 5 - The Community Favorite
The Raspberry Pi 5 needs little introduction. As the latest in the mainline Raspberry Pi family, it represents the culmination of years of refinement and the backing of the world's largest SBC community.
Specifications:
CPU: Broadcom BCM2712 (Cortex-A76, 4 cores, up to 2.4 GHz)
Architecture: ARM64 (aarch64)
Memory: 4GB or 8GB LPDDR4X
GPU: VideoCore VII
Connectivity: Dual-band Wi-Fi, Bluetooth 5.0, Gigabit Ethernet
The Raspberry Pi 5 brings significant improvements over its predecessor, including PCIe support for NVMe storage, improved I/O performance, and a more powerful GPU. The ecosystem around Raspberry Pi is unmatched, with extensive documentation, community support, and countless HATs (Hardware Attached on Top) for specialized applications.
Raspberry Pi CM5 - The Industrial Sibling
The Compute Module 5 takes the same BCM2712 chip as the Pi 5 and packages it in a compact, industrial-grade form factor designed for integration into custom carrier boards and commercial products.
Specifications:
CPU: Broadcom BCM2712 (Cortex-A76, 4 cores, up to 2.4 GHz)
The CM5 is fascinating because it shares the same CPU as the Pi 5 but often shows different performance characteristics due to different carrier board implementations, thermal solutions, and power delivery designs. For my testing, I used the official Raspberry Pi IO board.
Orange Pi 5 Max - The Multi-Core Beast
The Orange Pi 5 Max is where things get interesting from a pure performance standpoint. Built on Rockchip's RK3588 SoC, it features a big.LITTLE architecture with eight cores—four high-performance Cortex-A76 cores and four efficiency-focused Cortex-A55 cores.
The Orange Pi 5 Max is the performance king on paper, with eight cores providing serious parallel processing capabilities. However, as we'll see in the benchmarks, raw core count isn't everything—software optimization and real-world workload characteristics matter just as much.
For my testing, I chose a real-world workload that would stress both single-threaded and multi-threaded performance: compiling a Rust project in release mode. Specifically, I used my ballistics-engine project—a computational library with significant optimization and compilation overhead.
Why Rust compilation?
- Multi-threaded: The Rust compiler (rustc) efficiently uses all available cores for parallel compilation units and LLVM optimization passes
- CPU-intensive: Release builds with optimizations stress both integer and floating-point performance
- Real-world: This represents actual development workflows, not synthetic benchmarks
- Consistent: Each run performs identical work, making comparisons meaningful
Test Configuration:
- Fresh clone of the repository on each system
- cargo build --release with full optimizations enabled
- Three consecutive runs after a cargo clean for each iteration
- All systems running latest available operating systems and Rust 1.90.0
- Network-isolated compilation (all dependencies pre-cached)
Each board was allowed to reach thermal equilibrium before testing, and all tests were conducted in the same ambient temperature environment to ensure fairness.
The Results: Performance Showdown
Here's how the four systems performed in our Rust compilation benchmark:
Compilation Time Results
Performance Rankings:
Orange Pi 5 Max: 62.31s average (fastest)
Min: 60.04s | Max: 66.47s
Standard deviation: 3.61s
1.23x faster than slowest
Raspberry Pi CM5: 71.04s average
Min: 69.22s | Max: 74.17s
Standard deviation: 2.72s
1.08x faster than slowest
LattePanda IOTA: 72.21s average
Min: 69.15s | Max: 73.79s
Standard deviation: 2.65s
1.06x faster than slowest
Raspberry Pi 5: 76.65s average
Min: 75.72s | Max: 77.79s
Standard deviation: 1.05s
Baseline (1.00x)
Analysis: What the Numbers Tell Us
The results reveal several fascinating insights:
Orange Pi 5 Max's Dominance
The eight-core RK3588 flexes its muscles here, completing compilation 23% faster than the Raspberry Pi 5. The big.LITTLE architecture shines in parallel workloads, with the four Cortex-A76 performance cores handling heavy lifting while the A55 efficiency cores manage background tasks. However, the higher standard deviation (3.61s) suggests less consistent performance, possibly due to thermal throttling or dynamic frequency scaling.
LattePanda IOTA: Competitive Despite Four Cores
This is where things get exciting. The IOTA, with its quad-core Intel N150, finished just 6% behind the Raspberry Pi 5 and only 16% slower than the eight-core Orange Pi 5 Max. Consider what this means: a low-power x86_64 chip is trading blows with ARM's best quad-core offerings and remains competitive against an eight-core beast.
The IOTA's performance is even more impressive when you consider:
x86_64 optimization: Rust and LLVM have decades of x86 optimization
Higher clock speeds: The N150 boosts to 3.6 GHz vs. ARM's 2.4 GHz
Architectural advantages: Modern Intel cores have sophisticated branch prediction, larger caches, and more execution units
Raspberry Pi CM5 vs. Pi 5: The Mystery Gap
Both boards use identical BCM2712 chips, yet the CM5 averaged 71.04s compared to the Pi 5's 76.65s—a 7% performance advantage. This likely comes down to:
Thermal design: The CM5 with its industrial heatsink may throttle less
Power delivery: Different carrier board implementations affect sustained performance
Kernel differences: Different OS images and configurations
Raspberry Pi 5: Consistent but Slowest
Interestingly, the Pi 5 showed the lowest standard deviation (1.05s), meaning it's the most predictable performer. This consistency is valuable for certain workloads, but the slower overall time suggests either thermal limitations or less aggressive boost algorithms.
Beyond Benchmarks: The IOTA's Real-World Advantages
The IOTA (left) with DFRobot's PoE expansion board (right) - modular design for flexible configurations
Raw compilation speed is just one metric. The LattePanda IOTA brings several unique advantages that don't show up in benchmark charts:
1. Software Compatibility
This cannot be overstated: the IOTA runs standard x86_64 software without any compatibility layers, emulation, or recompilation. This means:
Native Docker images: Use official x86_64 containers without performance penalties
Commercial software: Run applications that only ship x86 binaries
Development tools: IDEs, debuggers, and profilers built for x86 work natively
Legacy support: Decades of x86 software runs without modification
Windows compatibility: Full Windows 10/11 support for applications requiring Windows
For developers and enterprises, this compatibility advantage is often worth more than raw performance numbers.
2. RP2040 Co-Processor Integration
The PoE expansion board showing power management and GPIO connectivity
The built-in RP2040 microcontroller (the same chip powering the Raspberry Pi Pico) is a game-changer for hardware projects:
Real-time GPIO: Hardware-timed operations without Linux scheduler jitter
Sensor interfacing: Direct I2C, SPI, and serial communication
Dual-core Cortex-M0+: Two 133 MHz cores for parallel hardware tasks
Arduino ecosystem: Use existing Arduino libraries with newer Arduino IDE versions (or LattePanda Leonardo compatibility for older IDE versions)
MicroPython support: Program in Python using the Raspberry Pi Pico SDK
Simultaneous operation: Main CPU handles compute while RP2040 manages hardware
Firmware updates: Easily reprogrammable via Arduino IDE or UF2 bootloader
This dual-processor design is perfect for robotics, industrial automation, and IoT applications where you need both computational power and reliable hardware control.
3. Storage Flexibility
The IOTA supports M.2 NVMe SSDs natively—no HATs, no adapters, just a standard M.2 2280 slot. This provides:
High-speed storage: 3,000+ MB/s read/write speeds
Large capacity: Up to 2TB+ easily available
Better reliability: SSDs are more durable than SD cards
Simplified setup: No SD card corruption issues
4. Display Capabilities
Rear view showing HDMI, USB 3.2, Gigabit Ethernet, and GPIO connectivity
With both HDMI 2.0 and USB-C DisplayPort Alt Mode, the IOTA offers:
Dual 4K displays: Power two monitors simultaneously
Single-cable solution: USB-C provides video, data, and power
Hardware video decoding: Intel Quick Sync for efficient media playback
5. Thermal Performance
Thanks to its 6W TDP and pre-installed heatsink, the IOTA runs cool and quiet. During my testing:
No thermal throttling observed across all compilation runs
Passive cooling sufficient for sustained workloads
Consistent performance without active cooling
Geekbench Cross-Reference
While my real-world compilation benchmarks tell one story, it's valuable to look at synthetic benchmarks like Geekbench for additional perspective:
The Geekbench results align with our compilation benchmarks: the IOTA shows strong single-core performance (higher clock speeds and architectural advantages) while the Orange Pi 5 Max dominates multi-core scores with its eight cores.
Power Consumption and Efficiency
While I didn't conduct detailed power measurements, some observations are worth noting:
LattePanda IOTA:
- 6W TDP design
- Efficient at idle
- USB-C PD negotiates appropriate power delivery
- Suitable for battery-powered applications
Orange Pi 5 Max:
- Higher power consumption under load due to eight cores
- Requires adequate power supply (4A recommended)
- More heat generation requiring better cooling
Raspberry Pi 5/CM5:
- Moderate power consumption
- Well-documented power requirements
- Active cooling recommended for sustained loads
For portable or battery-powered applications, the IOTA's low power consumption and USB-C PD support provide real advantages.
Use Case Recommendations
Based on my testing, here's where each board excels:
Choose LattePanda IOTA if you need:
Native x86_64 software compatibility
Windows or ESXi support
Arduino integration for hardware projects
Dual display output
NVMe storage without adapters
Strong single-threaded performance
Commercial software support
Choose Orange Pi 5 Max if you need:
Maximum multi-core performance
8K display output
Best price-to-performance ratio
Heavy parallel workloads
AI/ML inference applications
Choose Raspberry Pi 5 if you need:
Maximum community support
Extensive HAT ecosystem
Educational resources
Consistent, predictable performance
Long-term software support
Choose Raspberry Pi CM5 if you need:
Industrial/commercial integration
Custom carrier board design
Compact form factor
Same CPU as Pi 5 in SO-DIMM format
The DFRobot Ecosystem
DFRobot sent a comprehensive review package including the IOTA, active cooler, PoE HAT, UPS HAT, and M.2 expansion boards
One advantage of the LattePanda IOTA is DFRobot's growing ecosystem of accessories. The review unit came with several expansion boards that showcase the platform's flexibility:
Active Cooler: For sustained high-performance workloads
51W PoE++ HAT: Power-over-Ethernet for network installations
Smart UPS HAT: Battery backup for reliable operation
M.2 Expansion Boards: Additional storage and connectivity options
The complete accessory lineup - a testament to DFRobot's commitment to the platform
This modular approach lets you configure the IOTA for specific use cases, from edge computing nodes with PoE power to portable projects with UPS backup. The pre-installed heatsink handles passive cooling for most workloads, but the active cooler is available for applications that demand sustained high performance.
Final Thoughts: The IOTA Holds Its Ground
Coming into this comparison, I wasn't sure what to expect from the LattePanda IOTA. Could a low-power x86 chip really compete with ARM's best? The answer is a resounding yes—with caveats.
In raw multi-core performance, the eight-core Orange Pi 5 Max still reigns supreme, and that's not surprising. But the IOTA's real strength isn't in beating eight ARM cores with four x86 cores—it's in the complete package it offers:
Performance that's "good enough" for most development and computational tasks
Software compatibility that's unmatched in the SBC space
Hardware integration via the Arduino co-processor
Storage and display options that match or exceed competitors
Thermal characteristics that allow sustained performance
For developers working with x86-specific tools, anyone needing Windows compatibility, or projects requiring both computational power and hardware interfacing, the LattePanda IOTA represents a compelling choice. It's not trying to be the fastest SBC—it's trying to be the most versatile x86 SBC, and in that goal, it succeeds admirably.
The fact that it finished within 6% of the Raspberry Pi 5 while offering x86 compatibility, NVMe support, and Arduino integration makes it a strong contender in the crowded SBC market. DFRobot has created something genuinely different here, and for the right use cases, that difference is exactly what you need.
Specifications Summary
Feature
LattePanda IOTA
Raspberry Pi CM5
Raspberry Pi 5
Orange Pi 5 Max
CPU
Intel N150 (4 cores)
Cortex-A76 (4 cores)
Cortex-A76 (4 cores)
4x A76 + 4x A55
Architecture
x86_64
ARM64
ARM64
ARM64
Max Clock
3.6 GHz
2.4 GHz
2.4 GHz
2.4 GHz
RAM
Up to 16GB
Up to 8GB
4/8GB
Up to 16GB
Storage
M.2 NVMe, eMMC
eMMC, microSD
microSD, PCIe
M.2 NVMe, eMMC
Co-processor
RP2040 (Pico)
No
No
No
OS Support
Windows/Linux
Linux
Linux
Linux
Benchmark Time
72.21s
71.04s
76.65s
62.31s
Price Range
~$100-130
~$45-75
~$60-80
~$120-150
Disclaimer: DFRobot provided the LattePanda IOTA for review. All testing was conducted independently with boards purchased at my own expense for comparison purposes.