AI 资讯
The Day Our Web App Took 8 Seconds to Load (and How We Cut It in Half)
There is a quiet moment of panic every developer knows. You hit deploy, open the live site on your phone, and wait. One second. Two seconds. Four seconds. Still a blank white screen. A while back, I was working on a Next JS application that looked fast on high speed office Wi Fi. But when tested on a spotty mobile connection, it felt painfully slow. The initial page load was clocking in at nearly 8 seconds, and our main JavaScript bundle was a bloated 1.8 megabytes. Here is how we diagnosed the bloat, cut our load times by 47 percent, and the simple performance rules every developer should know. The Investigation: Where Was the Weight Coming From? When a website is slow, our first instinct is often to blame slow backend APIs or heavy database queries. But when I ran a performance audit, the backend was not the problem at all. The front door was just jammed with too much stuff. We were making three classic mistakes: First, we were packing for a long trip on a short walk. We were loading heavy charting libraries, complex admin tables, and pop up modals the second a user landed on the home page, even if that user only came to read a single line of text. Second, giant images were being served to tiny mobile screens, hogging precious bandwidth before any interactive buttons could even load. Third, a single state update at the top of our app was causing dozens of unseen child components to recalculate and re render unnecessarily behind the scenes. The Strategy: Trimming the Fat Instead of rewriting the entire codebase from scratch, we focused on three targeted fixes. 1. Don't Load It Until They Ask For It Why force a user to download a complex analytics chart if they have not even clicked on the dashboard tab yet? We split the app into smaller, independent code chunks. Now, the user downloads only the absolute bare minimum needed to view the immediate screen. The heavy features stay on the server until the exact moment the user interacts with them. 2. Smart Asset Delivery
AI 资讯
I built a tier list that re-rates 245+ AI tools every week — the automation behind it
AI tool reviews rot faster than anyone can rewrite them. A tool that was S-tier in March ships a broken pricing change in June, a "top 10" listicle from last year recommends products that no longer exist, and every directory slowly turns into a graveyard of dead links. I run AI Tier List , a bilingual (EN/KO) directory that ranks 245+ AI tools from S to D. My answer to review rot: don't re-review by hand. Make a pipeline re-rate everything weekly, and let humans only approve or reject. The architecture Everything runs on one weekly GitHub Actions cron (Next.js 16 + Prisma + Neon Postgres + Vercel): weekly cron (Sun 00:00 UTC) ├─ collect Google Trends per tool → trend scores ├─ collect OpenRouter usage rankings → weekly LLM leaderboard ├─ deactivate dead tools → site checks + trend slump ├─ discover new tools → search + AI triage ├─ re-evaluate tiers (LLM) → PendingUpdate rows └─ generate weekly blog draft → MDX The key design decision: the LLM never writes directly to the live site. Re-evaluations land in an approval queue ( PendingUpdate table). I review diffs in an admin panel and approve batches. The pipeline proposes; a human disposes. That one boundary is what keeps automated content from becoming automated garbage. Two collectors do the heavy lifting: Trend collector — Google Trends per tool, weekly. A tool in a sustained slump gets flagged; if its website also starts failing health checks, it gets deactivated automatically. Dead products remove themselves from the directory. OpenRouter collector — real token-usage data powers a weekly LLM leaderboard . No opinions, just "which models did people actually route traffic to this week," with usage share, pricing, and context length. What the tier actually means Each tool stores bilingual tierReason , strengths , and weakness fields, and the tier maps S→5 … D→1 into review schema markup. When the weekly re-evaluation moves a tool, the reason is regenerated with it — so the rating and its justification never drift a
AI 资讯
An Empty VAST Wrapper Is Schema-Valid in 4.4. It Was Not in 2.0.
A VAST wrapper with no AdSystem, no VASTAdTagURI and no Impression validates against the VAST 4.4 draft schema. The same document has been invalid in every version from 2.0 through 4.2. It is one line of XSD, and it is almost certainly a side effect of the CTV Ad Portfolio restructure rather than a decision anyone made on purpose. I have filed it with IAB Tech Lab. This post is the working, because the reproduction is short enough that anyone can check it in about a minute. The change In vast_4.4.xsd on master, both vastInLine_type and vastWrapper_type wrap their children in a single compositor: an xs:choice with minOccurs zero and maxOccurs unbounded. That looks harmless. It is the idiom people reach for when they want to say "these children may appear in any order". What it actually says is stronger than that. In XSD, the cardinality on the compositor governs the content model, and the minOccurs on the individual child elements only describes a single selection from the choice. Set the choice itself to zero-or-more and every constraint underneath it stops binding. So the children still declare minOccurs="1". They are still, in effect, optional. The compositor in question <!-- vast_4.4.xsd, vastWrapper_type and vastInLine_type --> <xs:choice minOccurs= "0" maxOccurs= "unbounded" > <xs:element name= "AdSystem" type= "vastAdSystem_type" /> <xs:element name= "VASTAdTagURI" type= "vastURIElement_type" /> <xs:element name= "Impression" type= "vastImpression_type" /> <xs:element name= "Creatives" type= "vastCreatives_type" /> <!-- ... --> </xs:choice> Three consequences, not one The empty wrapper is the headline, but the compositor gives up three separate guarantees at once. Each is reproducible with xmllint against the published schema. What now validates in 4.4 Everything is optional. An empty <Wrapper/> validates. So does an empty <InLine/> , with no AdSystem, no AdTitle, no Impression and no Creatives. Everything repeats. maxOccurs="unbounded" on the choice means any
AI 资讯
How much to share in a monorepo when building common features for web and mobile
This article is an English translation of the original Japanese article. When I added an Expo app to an existing Next.js web service, I initially wanted to share as much code as possible. In practice, types and business rules share well, while UI and runtime dependencies are easier to manage separately. SquadNote uses pnpm workspace and Turborepo, composed of apps/web , apps/mobile , and packages/* . Current split apps/ web/ Next.js, Cloudflare Workers mobile/ Expo, React Native packages/ api/ tRPC and Zod shared parts db/ Drizzle schema design-tokens/ Root workspace configuration is simple: packages : - " apps/*" - " packages/*" turbo.json manages only build and typecheck dependencies. Rather than adding custom build steps for sharing, I started with a structure where each package exports TypeScript sources. What I share The biggest benefit came from tRPC types. Mobile type-imports the web AppRouter , using the same input and output. import type { AppRouter } from " ../../apps/web/src/server/api/root " ; export const api = createTRPCReact < AppRouter > (); I also separated colors, spacing, and font sizes into @squadnote/design-tokens . export { colors } from " ./colors " ; export { spacing } from " ./spacing " ; export { radius } from " ./radius " ; Business rules like waitlists become sharing candidates as pure functions with no dependency on React or DB. They are easy to test, and results do not diverge between web and mobile. What I do not share I do not share screen components. Next.js DOM and React Native View have different interactions, accessibility, and layout constraints, even when they look similar. Authentication storage also differs: Web: NextAuth cookie session Mobile: SecureStore Bearer JWT Both reach the same API, but sharing the login screen and token storage would require handling each environment's concerns with many branches. Routing also has separate implementations for Next.js App Router and Expo Router. What I share is the meaning of organiza
AI 资讯
One Checkbox, Three Kinds of State in a Chrome MV3 Extension
I thought I had a settings bug. What I actually had was three different kinds of state pretending to be one boolean. While building a Chrome Manifest V3 email-tracker blocker, I expected a simple flow: you flip Gmail on in the settings, and the extension starts working in Gmail. That was the theory, anyway. The problem showed up when I was testing on a second Chrome profile. I'd enabled Gmail on my main profile, and Chrome Sync helpfully carried that preference over to the other one. But the optional permission for mail.google.com didn't come along — host grants live in the local profile and never sync. Profile number two now believed Gmail was enabled while lacking the host grant needed to inject the inbox content script or inspect its DOM. Depending on how you write your code, that's either a silent no-op or an extension quietly behaving as if access exists when it does not. Neither is great. Once I stopped and wrote it down, the picture got clearer. There are three separate things here: the inbox the user wants enabled, the host access Chrome has actually granted in this profile, and the dynamic DNR rules that are currently installed . Collapsing them into one flag is convenient. It's also wrong. The manifest is a menu, not an order The extension declares each webmail origin under optional_host_permissions . Every inbox gets activated on its own, and Chrome only asks the user for access when they turn that particular integration on. Here's the thing I had to internalize: declaring an optional origin means nothing by itself. Until the live grant exists, the extension has no business registering a content script for that inbox, poking at its DOM, or — by its own scoping policy — activating client-scoped blocking rules for it. Why bother with per-inbox prompts at all? Mostly trust. A tracker blocker that asks for all your webmail up front looks exactly like the thing it's supposed to protect you from. Asking for Gmail when you enable Gmail — and nothing more — is an
AI 资讯
X replaces its revenue-sharing program with ‘Original Content Rewards’
X is ending its controversial revenue-sharing program for content creators, which has seen numerous revisions under Elon Musk's reign. In its place, it's launching a new Original Content Rewards program on September 8th. To be eligible, creators must have at least 500 verified followers and at least 500,000 Home Timeline impressions from verified users in […]
AI 资讯
Build map guidance that follows the user without blocking pinch-to-zoom
A navigation map should help the user move through the world, not fight every gesture they make. I recently hit a deceptively simple bug while building field guidance in a React Native / Expo app: the route rendered correctly and the camera followed the current position, but users could not meaningfully zoom or pan while walking. They could pinch the map, but the next location update snapped the camera back to a fixed zoom. The map looked active. The experience felt broken. The cause: two camera owners The implementation combined two useful features: followsUserLocation={true} on the native map. animateCamera(...) after every location update, using a fixed walking zoom and pitch. Each feature was reasonable on its own. Together, they gave the camera two automatic owners and the user none. A pinch gesture changed the zoom for a fraction of a second. Then a GPS update arrived and our effect applied the navigation camera again. On iOS, native user-follow behavior added another layer of camera control. A better model: follow mode and explore mode The fix was not to stop navigation. Route progress, distance, bearing, breadcrumb recording and off-route detection should all continue regardless of what the user does with the map. Only the camera behavior should change. We now keep a small piece of local UI state: const [ cameraFollowing , setCameraFollowing ] = useState ( navigationActive ); useEffect (() => { if ( ! navigationActive || ! cameraFollowing || bearing == null ) return ; mapRef . current ?. animateCamera ( walkingCamera ( currentCoordinate , bearing ), { duration : 480 }, ); }, [ currentCoordinate , bearing , navigationActive , cameraFollowing ]); The native follow prop uses the same state: < MapView showsUserLocation followsUserLocation = { navigationActive && cameraFollowing } onTouchStart = { () => { if ( navigationActive ) setCameraFollowing ( false ); } } /> As soon as the user touches the map, the camera enters explore mode. Pinch, pan and rotation work n
创业投融资
X replaces ‘misaligned’ revenue sharing program with Original Content Rewards
X is winding down its existing Revenue Sharing program.
AI 资讯
# Why I’m Rewriting a PHP Extension in C23, Not C++
I forked the DataStax Cassandra driver when it stopped compiling on PHP 8 and most of its maintainers had already moved on. My first instinct was to write the new parts in C++. I built a Zend wrapper class, used RAII throughout, and put smart pointers around zval s—the whole modern setup. It introduced memory bugs that took me days to track down, and I did not get a meaningful benefit in return. So the driver is being rewritten in C23. I want to explain why, because “just use C++; it’s safer” is the reflexive answer. For a PHP extension, I no longer think it is the right one. This is not an argument that C++ is a bad language. In an application where I own the allocator, error model, and object lifetimes, std::vector and std::unique_ptr earn their keep. A PHP extension is different: the Zend Engine owns those rules, and its rules are written in C. The problem is not that C++ cannot call the Zend API. Plenty of extensions do. The problem is impedance: each abstraction has to be taught PHP’s lifetime rules, and the teaching code can become more complicated than the work it was meant to simplify. These are the four places where that cost me real debugging time. PHP owns the allocator PHP has its own memory manager. Request-scoped memory is allocated with functions such as emalloc , ecalloc , and safe_emalloc , then released with efree . Zend tracks that memory and normally reclaims what remains at request shutdown. Persistent allocations use a separate API because they have a different lifetime. Plain malloc and free —and therefore ordinary new and delete —sit outside that request-memory model. The moment I put a std::vector<zval> in an extension, its backing storage uses the C++ allocator unless I replace it. The obvious fix is a custom allocator: template < class T > struct PhpAllocator { using value_type = T ; template < class U > PhpAllocator ( const PhpAllocator < U >& ) noexcept {} PhpAllocator () noexcept = default ; [[ nodiscard ]] T * allocate ( std :: size_t
AI 资讯
I Turned an Android Phone Into a No-Root Cybersecurity Learning Workspace
I Turned an Android Phone Into a No-Root Cybersecurity Learning Workspace Most people don't look at an Android phone and think: "This could be a practical Linux, Python, networking, and cybersecurity learning environment." Usually, the assumption is that serious technical learning requires a laptop, a virtual machine, or dedicated hardware. I wanted to see how far I could push the opposite idea. What if the Android phone you already own could become a practical learning workspace without root access? That experiment eventually became DedSec . DedSec is a free and open-source project built around Android and Termux. Its goal is not simply to install a large collection of tools. The goal is to create an environment where someone can actually learn how the pieces fit together. Repository: https://github.com/dedsec1121fk/DedSec Official website: https://ded-sec.space/ Why Android? Android devices are incredibly capable machines. Even an older phone can provide: a Linux-like command-line environment through Termux Python Git package management networking utilities file manipulation scripting automation local development workflows And you can do a surprising amount without root access. The limitation isn't always the hardware. A bigger limitation is often knowing what to do with it. You can install dozens of packages, copy commands from tutorials, and still not understand what is actually happening underneath. That was one of the problems I wanted DedSec to address. More Than a Collection of Scripts There are plenty of repositories containing security scripts. That wasn't enough for what I wanted to build. Installing a tool doesn't automatically teach you: what problem the tool solves when you should use it what its output means what layer of the system is failing how networking concepts connect together why a command works why another command fails So DedSec gradually became an ecosystem rather than just a scripts directory. The project connects several things together:
AI 资讯
Building a Chrome Extension to Auto-Save Gemini Chat Logs using AI (Part 1)
This article was originally published on e-shikumi-labo . Hello, I'm Shin from e-Shikumi-Labo. How do you all manage your conversations with Gemini? When you manage to extract a useful response from the AI, have you ever thought, "I want to keep this somewhere"? It all started from a simple, practical desire in my daily work: "I want to automatically save useful conversations from Gemini to a spreadsheet before they fade away." So, borrowing the power of Generative AI (Gemini), I tried making my own personal Chrome extension. Over this four-part series, I will write about "systematized thinking"—the process of utilizing AI to build tools and independently maintaining them. In Part 1, I'll share the developmental dialogue process: "How did I instruct the AI, what information did I provide, and how did we complete the prototype?" 1. A Prompt That Says: "Don't Guess, Ask for the Information You Need" As the very first step in development, I threw this prompt directly at Gemini itself. "I want to save Gemini's responses to a spreadsheet using a Chrome extension. Tell me how to build it without using your imagination. If you need any specific information, please point it out." The key here lies in two constraints: "without using your imagination" and "point out if you need information." When you try to build a web data extraction tool using AI, the AI often tends to "guess" the internal structure of the webpage (like HTML tags and class names) on its own and write the code. And even when you test this supposedly completed code, you fall into the trap of it not working because it doesn't align with the actual screen structure. To avoid this trap, I explicitly communicated, "Don't guess on your own. If there's missing information, I want you to demand it from the human side." 2. A Game of Catch with AI Using DevTools When I threw this prompt, the AI returned the following response: AI: "Understood. To create code that works reliably while eliminating guesswork, please retr
科技前沿
Here Are the First Images of the Crater Left on the Moon by SpaceX’s Rocket
The Korea Aerospace Research Institute shared images that show the gash on the surface. It’s pretty small.
AI 资讯
Why Nodemailer Doesn't Work on Cloudflare Workers (And What To Do Instead)
A short explanation of a wall a lot of developers hit, why it isn't going away, and the five lines that replace it. You wrote a contact form. It worked locally. You deployed it to a Cloudflare Worker, or a Vercel Edge Function, or Deno Deploy, and got something like this: TypeError: Class extends value #<Object> is not a constructor or null Or, if you were luckier and got a useful error: Module not found: Can't resolve 'net' Then you spent an hour trying compatibility flags, polyfills, and bundler aliases. I want to save you the rest of that hour. This isn't a bug, and no amount of configuration will fix it. The actual reason Nodemailer's default transport is SMTP. SMTP is a protocol that runs over a raw TCP connection. To open one in Node.js, you call net.createConnection() . Cloudflare Workers don't run on Node.js. They run on V8 isolates — the same engine as Chrome, without the Node runtime around it. Vercel's Edge Runtime and Deno Deploy are built on similar principles. In that environment, there is no net module, because there are no raw TCP sockets. All networking is handled by managed infrastructure outside the runtime — Cloudflare's own writeup on bringing node:http to Workers is explicit about this: connection pooling, TLS negotiation, and egress IP management are handled at the system level, which is precisely why a subset of Node APIs can never be supported. So the chain is: No raw TCP → no net.createConnection() → no SMTP client → no Nodemailer. There's a second, smaller issue that often gets conflated with this one. Nodemailer issue #1621 points out that Nodemailer imports built-in modules without the node: prefix, which breaks the Workers build step. That one is fixable. But fixing it wouldn't help — you'd just move the failure from build time to runtime, where net still doesn't exist. Issue #1623 covers the broader edge-function problem. It's worth being clear that none of this is a knock on Nodemailer. It's an excellent library, actively maintained,
AI 资讯
I Built The Most Advanced Job Application Tracker
If you're actively applying for jobs, you probably know the struggle: Did I already apply to this company? Which resume version did I send? What salary did I mention when I applied? What was the budget mentioned in the job posting? When did I apply for this one? Which interviews are scheduled this week? What were the HR contact details again? What exactly were the requirements for this role? When is my next interview? Where did I even find this posting? How many of my applications are actually turning into interviews? Every one of those is answerable. The problem is that the answers are scattered across a spreadsheet, a notes app, your inbox, and your memory — and reassembling them takes longer than the follow-up you were trying to send. Spreadsheets are where most people start, and they hold up until somewhere around application number twelve. After that, searching, filtering, and keeping the thing current becomes its own small job — and a spreadsheet still won't tell you that six applications have been sitting in "Applied" for a month, or whether your last twenty went better than the twenty before them. That's why I built HireLoop — an advanced job application tracker meant to reduce the mental load of a job search rather than add to it. Live app: hireloop.yogeshchavan.dev — free to use, with a demo account if you'd rather look around before signing in. Check out the application demo video below: Check out some preview images of the application The short version With HireLoop you can: Track every application in one place — status, dates, salary, source, and links See where your search stands at a glance on a dashboard Move applications through a Kanban pipeline Search, filter, and sort as the list grows See interviews and deadlines on a calendar Analyse interview rates, offer rates, application trends, and which sources actually work Store notes, resume versions, HR contacts, salary details, and job links per application Mark the ones that matter as favourites Kee
科技前沿
Spokane Shows What the New Era of Wildfires Looks Like
With rising temperatures and drier conditions, fires are more likely to explode. And with more people living next to forests, the results can be catastrophic.
AI 资讯
Docker for Beginners: Images, Containers, Ports, and Volumes Explained
Docker for Beginners: Images, Containers, Ports, and Volumes Explained If you've ever followed a programming tutorial and seen something like: docker run ... you've probably wondered: What exactly is Docker doing? I had the same question when I started learning Docker. At first, I thought Docker was simply a way to "run applications in containers." But there is much more to it. Once I understood four concepts — images, containers, ports, and volumes — Docker became much easier to understand. So let's break it down from the beginning. What Is Docker? Docker is a platform for building, packaging, and running applications in isolated environments called containers . The basic idea is simple: Package an application together with the things it needs to run, and make that package portable. For example, imagine you build a Python application. Your application might depend on: Python 3.12 FastAPI Uvicorn Several Python packages Environment variables Certain system libraries On your computer, everything works. Then someone else downloads your project. They install a different Python version. A package is missing. Something behaves differently. Now you have: "It works on my machine." Docker helps reduce this problem by allowing you to define the environment your application should run in. The Four Concepts You Need to Understand Before learning Docker commands, understand these four things: Docker Image ↓ Docker Container ↓ Ports ↓ Volumes Let's look at each one. 1. What Is a Docker Image? A Docker image is a packaged, read-only template used to create containers. Think of it like a blueprint. For example: Docker Image │ ├── Ubuntu ├── Python ├── Application code ├── Dependencies └── Configuration An image contains the instructions and filesystem needed to create a container. You can download images from container registries such as Docker Hub. For example: docker pull nginx This downloads the Nginx image. You can see your downloaded images with: docker images You might see s
AI 资讯
Designing Clean Roblox GUIs: Grid, Contrast, and the 3-Click Rule
Designing Clean Roblox GUIs: Grid, Contrast, and the 3-Click Rule A Roblox game lives or dies by its UI. Players decide in seconds whether a game "feels" polished, and most of that feeling comes from the interface — health bars, inventory, shop buttons, loading screens. Yet a lot of Roblox GUIs are cluttered, low-contrast, and hard to tap on mobile. Here are the rules I keep coming back to. 1. Build on a grid, not by eye Roblox Studio's UIAspectRatioConstraint + UIGridLayout let you snap elements to a grid instead of dragging them freehand. Freehand layout looks fine on your monitor and breaks on every other screen. Pick a base cell size (e.g. 80×80) and make everything a multiple of it. A white health bar on a light background is invisible. Aim for at least 4.5:1 contrast on text and key elements. Dark UI over a dark game scene? Add a stroke — UIStroke is cheap and fixes readability instantly. 3. The 3-click rule A player should reach any core action (equip, buy, start) in 3 taps or fewer. If your shop is 4 menus deep, players leave before they spend Robux. Flatten it: one main HUD, one overlay panel per feature. 4. Mobile-first sizing Most Roblox players are on phones. A button that's comfortable on desktop is often too small to tap reliably on a 6" screen — minimum touch target ~48×48 px. Size with Scale , not Offset , so the UI scales with the viewport. 5. Reuse components Don't rebuild a button 12 times. Make one button template (Frame + TextLabel + UIStroke + UICorner + LocalScript) and clone it. This is the single biggest time-saver in Roblox UI work. The fast path If you'd rather not hand-roll every panel, a Roblox GUI maker lets you assemble common components and drop them straight into Studio — handy for prototyping before you commit to a fully custom design.
AI 资讯
The Headless Workspace: How Antigravity CLI Lowers the Neovim Learning Curve
A GUI IDE is great for local development, but it quickly falls apart when you transition to headless servers, low-power client machines, or remote clouds. If you pair an AI agent like Antigravity CLI with a native-first Neovim configuration, you can bypass complex setups entirely. Since the AI assistant is the one doing the heavy writing, refactoring, and saving of files, you don't need to be a Vim keyboard wizard to use Neovim. The editor simply becomes a fast, native terminal pane for inspecting the code and reviewing git diffs. By pairing the two, you can build a modern, high-performance workspace built on native features that runs perfectly in any terminal. Here is the backstory of how we ended up with this setup, and why going native-first in Neovim became our preferred remote development tool. 💻 The Backstory: From a Broken Screen to Ephemeral Cloud VMs My 10-year-old MacBook Pro recently had its screen break. It still works fine, but it is now permanently anchored to my desk with an external monitor. Buying a new laptop is too expensive right now, but I have an iPad that I use when traveling. To work from the iPad, I use Google Cloud Shell via the web browser. This allows me to write and inspect code using the Cloud Shell Editor and run Antigravity CLI . However, Cloud Shell has strict storage, memory, and CPU limits. As an Application Modernization, DevOps, and SRE developer, my projects are resource-intensive. I need to run multi-container environments like the Google Cloud Microservices Demo . Plus, next week I’m attending the Gemma Day Event hosted by the Google DeepMind team. This will be my first hands-on contact with Gemma, and after the event, I plan to continue testing how the model interacts inside a Kubernetes cluster, establishing observability for LLM-native metrics (like token throughput and response latency). I don't want to buy an expensive machine with a GPU just to test these setups. Instead, I want to spin up a GPU-enabled VM in Compute Eng
AI 资讯
What Linux actually does when you read a file
I asked Linux for one 4 KiB page from the start of a cold file. Four pages came back. I moved the same read one page further in, ran it again, and got one. Same file, same syscall, same kernel. The only thing that changed was where I started reading, and I spent twenty minutes assuming the tool I'd just written was miscounting. It wasn't. A read that starts at byte zero is treated as a promise. There's a branch in mm/readahead.c that reads, in full, if (!index) goto initial_readahead; . Offset zero means the kernel takes you for a program that's about to stream the whole file, and it fetches ahead immediately. Start anywhere else and you're assumed to be seeking randomly until a pattern proves otherwise. Nothing in my call said a word about my intentions. It inferred them from an offset. I spent two weeks on this sort of thing recently. Not for work, and not toward anything shippable. The short version of what I found is that a surprising amount of the machinery under a running program isn't carrying out instructions at all. It's guessing. The bench , because it changes how you should read every number here: an ext4 filesystem on a loop device, inside an OrbStack Linux VM on an Apple Silicon Mac, kernel 7.0.14, 4 KiB pages, read_ahead_kb at 128. That's a container sharing the host's kernel, not bare metal, and the host reclaims memory aggressively enough that a fully cached file can go cold in fifteen seconds. Reads came from dd ; the page-by-page counting came from a small C tool I wrote that mmap s a file and asks mincore() which of its pages are resident. You're not addressing the disk, you're addressing the page cache The model most of us carry is that read() goes and gets bytes off a device. It doesn't. It copies bytes out of the page cache into your buffer, and the page cache is just RAM the kernel uses to remember parts of files. If what you want is already there, no device is involved. If it isn't, the kernel fills the cache first and then copies. Either way
科技前沿
The 7 Best TV Shows to Stream This Month
Lanterns, Dark Matter, and the original Star Trek are just a few of the TV shows you should be watching right now.