今日已更新 166 条资讯 | 累计 40611 条内容
关于我们

标签:#performance

找到 236 篇相关文章

AI 资讯

How I Put PgCache in Front of a 16-Million-Row Postgres Database

Disclaimer: This is a side project, not a production story. The slow-query problem is real, but the database is synthetic data I generated to make it show up on demand. I have no connection to PgCache. Everything here is in a repo you can clone and run. I tested version 0.6.2. A handful of dashboard queries on one of my projects were fine for a year and then weren't: count users by tier, revenue grouped by country, best-selling products per category. Nothing exotic, just aggregates and joins over tables that had gotten big. The usual fixes didn't sit right with me. A materialized view means picking a refresh interval and serving slightly stale numbers in between. Redis in front of Postgres means writing and maintaining code that knows which cache entries to throw away on every write. A read replica just runs the same slow query on another machine. PgCache offers a different trade. It's a proxy that talks the Postgres wire protocol, so your app connects to it as if it were the database. It caches reads. And instead of expiring entries on a timer, it follows Postgres's replication stream and refreshes a cached result when the rows behind it change. That stream is the same feed Postgres uses to copy data to a standby server , a running log of every insert, update, and delete. The "no timers, no manual invalidation" part is the interesting claim. Here's how it held up. A database big enough to be slow First I needed a database where "slow" was real and not a rounding error. I wrote a seed script for a small e-commerce schema and filled it to about 16 million rows: Table Rows Notes users 1,000,000 10 countries; tiers 50% free / 33% pro / 17% enterprise products 2,000 10 categories orders 5,000,000 four statuses, random totals, spread over two years order_items 10,000,000 about two per order I added indexes on every foreign key and on every column the test queries filter or group by. That was on purpose. I wanted to compare PgCache against a Postgres that had been tuned p

2026-09-02 原文 →
开发者

Zstandard einfach erklärt in 2 Episoden — Episode 1

Episode 1: Was in einer ZST-Datei passiertEpisode 1: Was in einer ZST-Datei passiertZST-Dateien begegnen uns immer häufiger bei großen Downloads, Softwarepaketen, Backups und Serverdaten. Sie sind oft deutlich kleiner als die ursprünglichen Dateien und lassen sich trotzdem sehr schnell wieder entpacken. Doch wie funktioniert das? Warum werden Dateien komprimiert? Eine Datei besteht aus Daten. Je mehr Daten sie enthält, desto mehr Speicherplatz wird benötigt und desto länger dauert ihre Übertragung. Kompression versucht, dieselben Informationen mit weniger Daten darzustellen. Beim späteren Entpacken muss daraus wieder exakt die ursprüngliche Datei entstehen. Nach dem Entpacken ist die Datei Bit für Bit identisch mit dem Original. Es wird nichts weggelassen und nichts vereinfacht. Wiederholungen benötigen unnötig viel Platz Betrachten wir diesen Satz: Kleine Katzen kuscheln auf kleinen Kissen, junge Katzen kuscheln auf bunten Kissen und alte Katzen kuscheln auf weichen Kissen.Die folgenden Teile kommen mehrfach vor: A = Katzen kuscheln auf B = KissenWenn wir die wiederkehrenden Textteile durch die Variablen A und B ersetzen, können wir den Satz kürzer darstellen: Kleine A kleinen B, junge A bunten B und alte A weichen B.Damit ist der Text noch nicht vollständig. Zusätzlich müssen wir speichern, wofür A und B stehen: A = Katzen kuscheln auf B = KissenAus diesen Informationen lässt sich der ursprüngliche Satz wiederherstellen. Jedes A wird durch Katzen kuscheln auf und jedes B durch Kissen ersetzt. Das ist bereits die grundlegende Idee der verlustfreien Kompression: Wiederkehrende Daten werden nicht jedes Mal vollständig gespeichert. Stattdessen werden sie einmal gespeichert und anschließend durch kürzere Verweise ersetzt. ### Zstandard verwendet keine Variablen Unsere Variablen A und B dienen nur dazu, das Prinzip verständlich zu machen. Zstandard versteht weder Wörter noch Sätze. Es weiß nicht, was Katzen oder Kissen sind. Für das Programm besteht eine Datei lediglich

2026-09-01 原文 →
AI 资讯

Sizing a session broker: the unit is concurrent sessions, and the bottleneck is not the CPU

Disclosure: the numbers below are from Tessera, which I work on. The reasoning applies to any proxy that sits in a session path. Every vendor page in this category says something like "scales to thousands of users". It is a useless number, because a user who is not connected costs nothing. What costs something is a session that is open right now. So here is the arithmetic instead, with the method, so you can check it against whatever you are evaluating. The unit The controller does not know how many engineers you employ and does not care how many targets are registered. It knows how many sessions are open. The planning rule that has held up for us: on a normal working day, 10–20% of a team is connected at once. A 200-person engineering organisation is 20–40 concurrent sessions, not Size for your own observed peak, but if you are estimating from scratch, start there. This matters because the difference between the two numbers is the difference between a 512 MB VM and an argument about whether you need a cluster. Memory A proxied session is mostly buffers. In our case: about 256 KB of copy buffers, plus 6 to 10 goroutines at roughly 8 KB of stack each. Call it 320 KB per session. The base Go process is about 30 MB. So 200 concurrent sessions is 200 × 320 KB ≈ 64 MB of live data, plus 30 MB base, ≈ 94 MB. Except that is not what RSS will show you, and this is the part people get wrong when they size Go services. Go does not hand memory back to the operating system promptly, and at the default GOGC=100 the collector lets the heap grow to roughly twice the live set before collecting. So resident memory settles at about double the arithmetic. Concurrent sessions vCPU Expected resident Provision up to 50 1 ~90 MB 512 MB 50–200 2 ~190 MB 1 GB 200+ 4 ~380 MB 2 GB The gap between the last two columns is headroom for spikes, not a hidden cost. A controller serving 200 sessions really does use a couple of hundred megabytes. Our Helm chart ships requests: 256Mi and limits: 1Gi ,

2026-09-01 原文 →
AI 资讯

What I Learned Partitioning a Billion-Row Table in Production

Adding an index stops working eventually. Here's what we did when a nationwide logistics platform's core table crossed a billion rows — and the parts nobody warns you about. There's a specific moment in a backend engineer's life when the usual advice stops working. A query gets slow. You check the execution plan, you add an index, it gets fast again. This works for years. It works so reliably that it starts to feel like a law of nature. Then one day you add the index and nothing happens. Or worse — the index takes six hours to build, locks the table while it does, and the query is still slow at the end of it. That's roughly where we were on a nationwide logistics platform processing tens of thousands of orders a day. The tracking events table — one row per scan, per parcel, per status change — had crossed a billion rows. Every parcel generated a dozen or more events on its journey. The table only ever grew. This is what we did about it, and more usefully, what nobody told us beforehand. First: are you sure you need this? Partitioning is not a performance trick you reach for when a query feels sluggish. It carries real operational cost, and most tables that people want to partition should just be indexed properly. Some honest signals that you're actually at the boundary: Your indexes no longer fit comfortably in memory, so index reads hit disk Index maintenance — REINDEX, VACUUM, ANALYZE — takes so long you can't schedule it Deleting old data is impossible in practice, because a DELETE of a hundred million rows will destroy your write throughput for hours Your queries almost always filter on a single obvious dimension, usually time That last one matters more than the rest. Partitioning only helps if your access pattern lines up with how you split the data. If your queries hit every partition anyway, you have added complexity and gained nothing. For us the alignment was clean: nearly every query on the events table was scoped to a date range. Operations dashboards loo

2026-08-31 原文 →
AI 资讯

The Architecture Behind CoxOutage.us

When an internet outage hits, users immediately turn to their phones to find out if it's just them or a widespread network issue. Because they are often relying on spotty cellular data, any tracking site needs to load instantly and deliver highly localized information. I recently launched CoxOutage.us to map and track Cox Communications disruptions. Here is a breakdown of the technical and SEO strategies I used to build it. Performance & Traffic Handling Outage trackers face a unique challenge: they get zero traffic when things are fine, and massive, sudden spikes the minute a service goes down. Aggressive Caching: I implemented LiteSpeed Cache combined with Memcached for object caching. This ensures that database queries are kept to an absolute minimum when a sudden wave of users hits the site. Edge Delivery: Everything sits behind Cloudflare for DNS management and edge-level caching, ensuring the server (hosted via InterServer) doesn't get overwhelmed during regional outages. Scalable SEO & Routing Architecture The biggest hurdle was capturing local search intent accurately. Hyper-Specific URL Slugs: Initially, you might think to use a simple routing structure like /los-angeles . However, I found that using full keyword slugs—such as /cox-outage-los-angeles —significantly boosted visibility and search performance. Automated Indexing & Schema: I utilized the Google Indexing API to push new city landing pages instantly. Paired with Rank Math, the site generates precise schema markup so search engines understand the real-time nature of the status updates. Looking Forward Right now, the focus is on scaling out the localized landing pages and refining the automated reporting pipeline. If you have experience building high-traffic, real-time alert systems or handling sudden traffic spikes, I’d love to hear your approach. Check out the live project here: CoxOutage.us Feedback and suggestions are always welcome!

2026-08-30 原文 →
AI 资讯

Launching vizcrush: Three Beliefs My Benchmarks Killed

It's the week before vizcrush goes public, and I have two files open side by side. On the left, the launch copy: the JS core beats the most popular npm downsampling package by 32×, "and WASM adds another 5-10x on top." On the right, the repo's own benchmark control run: wasm/js ≈ 1.00× . One million points, same algorithm, same machine. Parity. I go looking for the measurements behind the claim. Half of it holds up: the 32× JS comparison has a result file (1.72ms against 55.52ms, real). The claimed additional 5-10× from WASM has nothing behind it, and the repo's own control run contradicts it. That afternoon set the shape of the whole launch: before anything shipped, every performance claim would either get a measurement behind it or get deleted. Three beliefs didn't survive. Each one got a public retraction, written up as an ADR in the repo. vizcrush is a set of data primitives for browser visualization (downsampling, binning, spatial indexing, streaming sketches), written in Rust, compiled to WebAssembly, with a pure-JS core behind the same API as a fallback and explicitly selectable backend. It went open source this week: the repo and the book are public, and all 11 packages are live on npm. npm install @vizcrush/core @vizcrush/downsample This is a launch story about turning benchmark results into product policy: claims, documentation, and WebGPU policy follow the measurements, while WASM dispatch stays availability-based pending further investigation. One scope note before the data. Every result here is workload-specific: LTTB (Largest-Triangle-Three-Buckets, the downsampling algorithm that picks, per bucket, the point that best preserves the visual shape of the line) is downsampling, the stats kernel is a reduction, and bin2d is histogramming. Which backend wins is algorithm- and engine-dependent, so none of what follows is a library-wide WASM-versus-JS verdict. It is three specific workloads measured on specific engines, with the claims and documentation follo

2026-08-30 原文 →
AI 资讯

I built a C library that avoids recomputing unchanged state — here are the reproducible benchmarks

Most performance optimization focuses on making each operation faster. HKD Kernel approaches a different question: What if most of those operations did not need to execute at all? I’ve been working on HKD Kernel, a native C library for exact sparse and incremental computation. The target workload looks like this: A large computation has already been evaluated. Only a small subset of the inputs changes. The dependency structure tells us which results can actually change. HKD recomputes those affected regions instead of repeating the entire calculation. The important word is exact. The optimized result must equal the result of full recomputation. What the benchmark measures The repository contains reproducible benchmarks comparing full recomputation with the HKD incremental path. Across the benchmark suite currently documented in the repository, the measured mean speedup is roughly 18,000x. That requires an important qualification: This does not mean HKD makes arbitrary programs 18,000x faster. It means that on workloads with sparse changes and reusable state, avoiding redundant computation can produce extremely large reductions in work. That distinction is important enough that I built the repository around reproducibility rather than a black-box benchmark claim. What HKD Kernel is not HKD Kernel: does not replace the macOS XNU kernel does not modify CPU microcode does not disable SIP does not change processor ALU hardware It is a user-space native computation library. Where I think this model is useful The workloads I’m most interested in include: dependency graphs incremental build systems large simulations with sparse updates optimization systems financial/risk recomputation logistics and scheduling cached numerical pipelines The real question is not “how fast is HKD?” It is: How much of your current computation is being repeated even though the inputs affecting it never changed? I’d especially like developers to try to break the benchmark assumptions or suggest w

2026-08-30 原文 →
开发者

Cloudflare KV for Session Caching in Multi-Tenant FastAPI: Reducing PostgreSQL Load Without Redis Complexity

Cloudflare KV for Session Caching in Multi-Tenant FastAPI: Reducing PostgreSQL Load Without Redis Complexity Every SaaS I've built hits the same wall: session validation on every request hammers PostgreSQL. You add Redis, suddenly you're managing another service, debugging cache invalidation, and paying for redundancy you don't need. Then I discovered Cloudflare KV sits between your users and origin server. It's not a replacement for PostgreSQL—it's a read cache positioned at the edge that auto-syncs on writes. For multi-tenant session and permission data, this eliminates 60–80% of auth-related database queries without the operational complexity of Redis. This is the approach I use in CitizenApp. Here's why it works, how to implement it, and where I nearly broke production. Why Cloudflare KV Beats Redis for Session Caching Redis requires: A separate service deployment (Render, AWS ElastiCache) Connection pooling logic in your app Cache invalidation strategies you'll get wrong Monitoring for memory leaks and eviction Cost that scales with your hot data size Cloudflare KV requires: A binding in your edge worker (one line of config) Simple key-value storage at 200+ edge locations Automatic TTL expiration Zero operational overhead—Cloudflare manages it Here's my honest take: I prefer KV because I don't have to think about it. My workers validate JWT tokens and fetch session data from KV before even routing to my FastAPI origin. Cache misses flow to PostgreSQL and write back to KV. No connection pools. No eviction policies. No debugging Redis memory fragmentation at 3 AM. The tradeoff? KV is slower than in-memory Redis (ms vs microseconds), but for session lookups happening 200+ times per second per user at global scale, edge-cached responses beat origin-fetched ones every time. Architecture: Edge Validation + Origin Sync Your flow looks like this: Request hits Cloudflare Worker Worker checks KV for session + permissions (hit = serve immediately) KV miss → fetch from Fas

2026-08-29 原文 →
AI 资讯

Subqueries vs CTEs: Query Optimizer Internals & Memory Spooling Explained

Many engineers believe Common Table Expressions (CTEs) are always faster than subqueries. In modern SQL Server (and PostgreSQL), that is a myth . Here is what actually happens under the hood: 1. Inlining & The Query Optimizer By default, the SQL optimizer treats standard CTEs and derived tables (subqueries) almost identically: The engine expands both into the same relational tree. They generate the exact same execution plan and I/O cost . -- Pattern A: Derived Table (Subquery) SELECT DeptID , EmpName , Salary FROM ( SELECT DeptID , EmpName , Salary , DENSE_RANK () OVER ( PARTITION BY DeptID ORDER BY Salary DESC ) AS rnk FROM Employees ) RankedData WHERE rnk <= 2 ; -- Pattern B: Common Table Expression (CTE) WITH RankedData AS ( SELECT DeptID , EmpName , Salary , DENSE_RANK () OVER ( PARTITION BY DeptID ORDER BY Salary DESC ) AS rnk FROM Employees ) SELECT DeptID , EmpName , Salary FROM RankedData WHERE rnk <= 2 ; 2. When CTEs Truly Win: Readability & Pipeline Stacking: You can chain 5 CTEs sequentially without deeply nested pyramid brackets. In-Place Deduplication: In SQL Server, you can run DELETE directly on a CTE, and it deletes duplicate rows straight from the real underlying table! WITH DuplicateCleaner AS ( SELECT CustomerID , Email , ROW_NUMBER () OVER ( PARTITION BY Email ORDER BY RegistrationDate ASC ) AS rn FROM Customers WHERE Email IS NOT NULL ) DELETE FROM DuplicateCleaner WHERE rn > 1 ; -- ✅ Clean in-place deletion! 3. The Big Trap (Spooling Overhead): If you reference the same CTE multiple times in a query (e.g. CTE_A JOIN CTE_A ), SQL Server may execute the underlying CTE query multiple times or create a Lazy Spool in tempdb . -> Fix: For heavy multi-million row reuse, use a Temporary Table ( #TempTable ) with an explicit Clustered Index instead! 💡 How do you choose between CTEs, Temp Tables, and Subqueries in your pipelines? 💼 Connect on LinkedIn: linkedin.com/in/arpitmbangre

2026-08-29 原文 →
AI 资讯

How to Open a 50GB Log File — and Reopen It in 0.05 Seconds. A klogg Alternative, Benchmarked

If you searched for a klogg alternative , you probably already know klogg is good. It is fast, it is free, it is open source, and it runs on Windows, macOS and Linux. Most people who go looking for something else are not unhappy with klogg as a viewer. They are unhappy with one specific moment in their day: Opening the file again. You investigated a 48GB log yesterday. You closed it. This morning your colleague asks about a different error, and you have to wait through the whole index build a second time. On a USB HDD that is nine minutes of staring at a progress bar — and while it builds, klogg only shows you the beginning of the file. That is the problem this article is about. Below is a measured comparison on a real 47.73GB file, including the rows where klogg wins . The test File OpenStreetMap Japan japan-latest.osm — 47.73 GB, 892,239,125 lines Machine MacBook Air / Apple M4 (10 cores) / 32GB RAM Storage (measured with dd ) USB HDD 0.10 GB/s / USB SSD 0.41 GB/s / Internal SSD 3.29 GB/s Versions klogg 24.11.0 / UwView Pro Search hit counts were verified to match exactly across klogg, UwView Pro, and a direct search of the raw file — so we know both tools are answering the same question. The numbers klogg 24.11.0 UwView Pro Ratio First open HDD ~9 min / USB SSD ~110 s / Internal SSD ~15 s — every time HDD 10.6 min / USB SSD 138.5 s / Internal SSD 23.3 s — first time only klogg wins Reopening Same as the first open (re-indexes every time) 0.01–0.07 s ~1,250–50,000x Search, literal "Tokyo" ~585 s / 120–135 s / 15–20 s 74.8 s / 14.3 s / 5.1 s ~7.8x / ~9x / 3–4x Search, regex "Tok[yi]o" ≈ literal (I/O bound, pattern-independent) 29.8 s (USB SSD) / 11.0 s (Internal SSD) ~4.4x / ~1.5x Disk used to keep the file 48 GB (original required) 5.3 GB (original can be deleted) 1/9 Two things are worth saying plainly. klogg opens the file faster the first time. UwView Pro is slower on the first open because it is building a compressed cache while it reads. That is a real cost a

2026-08-29 原文 →
开发者

A Practical Guide to React Performance

React is fast by default, until it isn't. The good news is that the vast majority of real-world performance issues trace back to a small set of patterns. Fix those, and you rarely need exotic optimizations. Measure before you optimize The first rule of performance work is to never guess. Use the React Profiler and the browser's performance panel to find what actually renders, and how often. Premature optimization Wrapping every component in memo and every value in useMemo adds complexity and can make things slower. Optimize the hot paths you have measured, not the ones you imagine. Avoid unnecessary re-renders A re-render isn't inherently bad, but cascading re-renders of expensive subtrees are. The most common culprit is passing a freshly-created object or function on every render. `// ❌ A new array + handler every render breaks memoized children function ProductList({ products }) { return ( - p.inStock)} onSelect={(id) => track(id)} /> ); } // ✅ Stabilize derived data and callbacks function ProductList({ products }) { const inStock = useMemo( () => products.filter((p) => p.inStock), [products], ); const handleSelect = useCallback((id) => track(id), []); return ; } ` Memoize the right things React.memo , useMemo and useCallback are tools for keeping referential identity stable across renders. Reach for them when: a child component is expensive to render, and it receives props that would otherwise change identity every render. Better still, let the React Compiler handle memoization for you. Adding it is a single dependency: npm install babel-plugin-react-compiler Ship less JavaScript The fastest code is the code you never send. Code-splitting and lazy loading keep the initial bundle small. `import { lazy, Suspense } from 'react'; const Editor = lazy(() => import('./Editor')); export function Panel() { return ( }> ); } ` Move work to the server With React Server Components, data fetching and heavy rendering can happen on the server, shipping only the resulting HTML an

2026-08-28 原文 →
开发者

Next.js SEO: An App Router Playbook That Ranks

Next.js gives you almost everything you need to rank well out of the box, and most teams still ship sites that Google struggles to read. The framework is not the problem. The problem is that SEO gets treated as a final checkbox instead of an architectural decision, so metadata ends up scattered, content renders on the client, and the structured data never gets written. The App Router changed how all of this works. The generateMetadata function, file-based conventions for sitemap.ts and robots.ts , and Server Components as the default each remove a class of SEO bug that used to be common in the Pages Router. But they only help if you use them deliberately. This is the playbook we follow when we build a Next.js site that has to rank, the same approach behind this site. It is opinionated and concrete: where to put metadata, which files to ship, how to handle structured data and multiple languages, and why Core Web Vitals is an SEO feature rather than a performance afterthought. None of it requires a plugin. Render on the server so Google sees real HTML The single biggest SEO win in Next.js is also the easiest to get wrong: make sure your indexable content is in the HTML on the first byte. Googlebot will execute JavaScript, but it does so on a delay and with no guarantees. Content that depends on a client-side fetch can be missed, indexed late, or indexed empty. Server Components are the default in the App Router, so this is mostly about not opting out. Keep &#x27;use client&#x27; at the leaves of your tree, on the button that needs an onClick , not on the page that holds your copy. Fetch your data in the Server Component and pass the rendered result down. If you can view the page source and read your headline and body text without JavaScript, you are in good shape. Master the Metadata API instead of next/head In the App Router you never touch next/head . Every route exports either a static metadata object or a dynamic generateMetadata function, and Next.js merges and d

2026-08-28 原文 →
AI 资讯

Migrating to Next.js 16: A Practical Upgrade Guide

Next.js 16 is the biggest release since the App Router landed, and the upgrade is not a one-line bump. The caching model changed shape, params and searchParams are now promises everywhere, Turbopack runs your builds by default, and middleware.ts is on its way out in favour of proxy.ts . None of that is hard on its own. The trouble is that the changes touch almost every dynamic route in a real app at once, so a rushed upgrade tends to fail in a dozen small places rather than one obvious one. We run this site on Next.js 16, and we have moved client projects across the same gap. The pattern that works is boring and reliable: read the codemod output, fix the async APIs first, decide your caching strategy deliberately instead of letting the old implicit behaviour leak back in, then clean up the renamed files. This guide walks through that order, with the specific gotchas that cost the most time. If you are still on Next.js 13 or 14, the same steps apply, you just have more of them to work through. Run the codemod, then read what it could not fix Start with the official upgrade command. It pulls the right versions of next , react , and react-dom , and runs the codemods that handle the mechanical rewrites for you. npx @next/codemod@latest upgrade latest The codemod is good, but it is not magic. It will happily wrap your params access in await where the shape is obvious, and skip anything indirect, a params object passed into a helper, destructured two functions deep, or read inside a generateMetadata you wrote by hand. Treat the codemod as the first 80%, not the finish line. Once it has run, do a clean install and a type check before you touch anything else. With typescript.ignoreBuildErrors set, as it is on many projects, the build will not catch these for you, so run the type checker yourself. rm -rf node_modules .next && npm install && npx tsc --noEmit The errors that come back are your real to-do list. Most of them will be the async API change, which is the next sectio

2026-08-28 原文 →
AI 资讯

Your Free AI Server Has a Ceiling. Measure It in 30 Minutes Before the Team Does

Tuesday, 10:47 AM. Fourteen developers open their IDE extensions at once, and the shared AI server starts returning timeouts. Nobody planned for the morning spike. The free tier was announced on Monday, the team adopted it by Tuesday, and the first capacity incident happened before lunch. This article is a 30-minute load-test workflow for teams that just received access to a free hosted AI server. The goal is not to benchmark model quality. The goal is to find the concurrency ceiling before your team does — the hard way. The Free Server Is a Shared Resource Now MonkeyCode is an open-source AI coding project that offers free models and a free server. The offer is attractive for the same reason it is dangerous: it removes the two usual adoption barriers — API billing and self-hosting operations — and turns the server into a shared team resource overnight. Disclosure: This article was prepared as part of MonkeyCode's product outreach. A shared resource without a measured ceiling behaves like a shared database without connection pooling. It works in the demo, degrades under load, and fails at the worst possible moment: the morning standup, the release freeze, the day before the demo. The failure mode is not what most teams expect. It is not the token quota. It is latency collapse. Requests queue, timeouts cascade, and the IDE extension retries, which adds more load. The server does not die; it just becomes unusable. The Math: Little's Law for AI Requests Before writing any test code, define the model. Little's Law states that the average number of requests in a system equals the arrival rate multiplied by the average service time: L = λ × W L — average requests in the system (concurrency) λ — arrival rate, requests per second W — average service time per request, in seconds For an AI server, W is dominated by model inference time. A single code-generation request can take 10 to 40 seconds on a shared free server, depending on the model and the prompt length. That change

2026-08-28 原文 →
AI 资讯

The Audit's Blind Spot: I Weighed the Build, Not the Page

I published a post called "I Audited My Own Portfolio and Found 20 Problems" . It was an inventory: I went through my own site — a React 19 + Vite SPA with Sanity as the CMS — wrote down everything that was wrong with it, fixed what mattered, and put the before and after numbers next to each item. If you haven't read it, the only part that matters here is the methodology, and one line of it in particular: I went through the build output chunk by chunk in build/assets/ . I called that the step that hurts and the one most people skip. I still think that is true. It is also the step that guaranteed I would miss the largest thing wrong with the site. The step that worked Weighing the build output worked exactly as advertised. Finding 1 of that audit was an unoptimized PNG of a developer illustration on /gabriel-abreu , my contact page, 993 KB, sent to every visitor who landed there. It went to 23 KB. A second image, the cutout of me that sits in three different greetings, went from 358 KB to 45 KB. Those two are bundled assets. A component imports one: import p from " ../assets/developer-illustration.webp " ; Vite follows that import, hashes the file, and emits it into build/assets/ . After the build it is a file on disk with a size. Listing the directory finds it. Sorting the listing by size finds it first. There is no way to ship it and not have it show up in that step. So the method was sound within its domain: both of those images are bundled assets, and the step found both. On August 23 I opened the blog index in a browser and watched what it actually requested. Sixteen post covers, 9.88 MB. None of that could have appeared in the audit. Not because I was sloppy that day — because of where those bytes come from. Two lifecycles A bundled asset exists at build time. An import makes it a build input, the bundler makes it a build output, and anything that reads the build output sees it. A CMS image is never a build input. Nothing imports it. It arrives as a string in a

2026-08-26 原文 →
AI 资讯

Did FP8 make the model dumber? A per-prompt regression check for quantized serving

FP8 gave us a clean 1.5x on Qwen3-8B serving throughput on an RTX PRO 6000 Blackwell (1,725 to 2,597 tok/s at concurrency 32, vLLM). The uncomfortable question is always the same: did the model get dumber. This post is the exact check we ran before recommending the switch, with numbers, so you can run the same one. Why "run an eval suite" is usually the wrong first answer Standard benchmarks (MMLU and friends) are noisy instruments for quantization deltas at 8B scale. Score movement inside the error bars tells you nothing about whether YOUR prompts changed behavior. What you actually want to know is narrower: on the workload you serve, does the FP8 checkpoint produce materially different outputs than BF16, and are any of the differences wrong. That is answerable directly, cheaply, and per prompt. The method Both configurations run the same fixed workload: 20 prompts covering reasoning, code, summarization, translation, extraction, classification, math, and instruction following. Greedy decoding, temperature 0, 256-token cap, streamed. Greedy matters: it removes sampling noise, so any output difference is attributable to the numerics. Then a three-stage comparison: Byte equality. outputs_bf16[i] == outputs_fp8[i] . Anything identical is settled. Similarity triage. For non-identical pairs, difflib.SequenceMatcher.ratio() sorts near-identical wording drift from real divergence. Side-by-side review under a written rubric. Every non-identical pair gets read. The rubric asks one question: is there a factual or numerical claim that one precision gets right and the other gets wrong. Wording changes, reordering, and equally-defensible readings are recorded but not counted as regressions. The core loop is small: import difflib , json bf16 = json . load ( open ( " vllm_bf16_conc1.texts.json " )) fp8 = json . load ( open ( " vllm_fp8_conc1.texts.json " )) for i , ( a , b ) in enumerate ( zip ( bf16 , fp8 )): if a == b : print ( i , " identical " ) continue r = difflib . Sequenc

2026-08-26 原文 →
AI 资讯

Podcast: The Human Edge: Why Brownfield Codebases Need Mob Programming, Not Just AI Vibes

Asgaut Mjølne Söderbom and Ola Hast discuss the evolution of their software engineering practices past continuous deployment and pair engineering. The conversation continues where it left off in the previous episode and focuses on the experiments in adopting Claude Code and the reasons why they consider it good for everything else, but not coding. By Asgaut Mjølne Söderbom, Ola Hast

2026-08-24 原文 →
AI 资讯

Benchmarking Zippers in Haskell

In the previous post , we explored zippers and their applications in functional programming. In this post, we benchmark their performance against a root-based approach. Two Approaches We define a simple tree data structure and the naive root-based approach for traversing and modifying the tree. data Tree = Atom ! Int ! String | Object ! Int ! ( Map String Tree ) deriving ( Show , Eq , Generic , NFData ) access :: [ String ] -> ( Tree -> Tree ) -> Tree -> Tree access [] f t = f t access ( k : ks ) f ( Object vers ts ) = Object vers $ Map . alter modifyChild k ts where modifyChild Nothing = error "Invalid path to access" modifyChild ( Just child ) = Just $ access ks f child access _ _ _ = error "Invalid path to access" Then we implement the zipper data structure and its operations for traversing and modifying the tree. data Zipper = Zipper { focus :: ! Tree , breadcrumbs :: [ Crumb ] } deriving ( Show , Eq , Generic , NFData ) type Move = Zipper -> Zipper data Crumb = Crumb { holeKey :: ! String , storedVers :: ! Int , siblings :: ! ( Map String Tree ) } deriving ( Show , Eq , Generic , NFData ) goDown :: String -> Zipper -> Zipper goDown k ( Zipper ( Object vers ts ) bs ) | ( Just child , siblings' ) <- Map . updateLookupWithKey ( \ _ _ -> Nothing ) k ts = Zipper child ( Crumb k vers siblings' : bs ) goDown k ( Zipper f _ ) = error $ "Cannot go to child '" ++ k ++ "' of tree: " ++ show f goUp :: Zipper -> Zipper goUp ( Zipper t ( Crumb key vers siblings' : bs )) = Zipper ( Object vers ( Map . insert key t siblings' )) bs goUp ( Zipper _ [] ) = error "Already at the top" Benchmark Design Each benchmark performs 100,000 operations. Three full trees are generated with the following shapes: Depth × width nodes Children per Map 5 × 16 1,118,481 16 10 × 4 1,398,101 4 20 × 2 2,097,151 2 Here, depth counts edges from the root. All three trees have exactly 1,048,576 leaves, but their shapes differ. The workloads are: Random lookup. Choose a path by selecting its depth uniform

2026-08-24 原文 →
AI 资讯

When Python is Too Slow

Python is a perfect language for Agile development, where requirements might change on the go. Especially if you are in a startup business, you will need to experiment and change things fast. However, Python is an interpreted language, and in certain situations you might need faster performance than what an interpreted language can provide. A common practice in these cases is using python-to-binary bindings, where the binary code is built with Rust, C++, or Go. In this article, I will explore bindings to Rust-based code. How do the bindings work The idea behind bindings is that you create a module with functions of a specific domain in a language that compiles to binary, and build it as a C-compatible dynamic library ( .so on Linux, .dylib on macOS, .dll on Windows). Then a Python wrapper is built as a Python package and installed together with the dynamic library, allowing you to import and use functions that pass control to the corresponding functions in the dynamic library. On some occasions, classes can be used instead of functions. If any parameters are complex, they must be serialized in the wrapper and passed to the dynamic library as a JSON string or as a set of individual primitive parameters. An experiment with benchmarks To try this Python-Rust communication, I vibe coded an experiment that reads a large CSV file and builds a new one with duplicates stripped out based on specified column indexes. In my test case, it was a 3 MB CSV file with data about European NGOs for the donation platform I am building, where I wanted to remove the NGOs that don't have website URLs listed. As benchmarked, the file was processed 4.3x faster with the Rust binding than directly with Python. Here is the repo to get a first glimpse into the code and structure. What is there to know about Rust A few things about Rust: Rust packages are built with Cargo, which is the equivalent of pip, virtualenv, and setuptools combined. A single package is called a crate, and it can be publi

2026-08-23 原文 →