frontpage.
newsnewestaskshowjobs

Made with ♥ by @iamnishanth

Open Source @Github

fp.

Open in hackernews

The Cost of a Closure in C

https://thephd.dev/the-cost-of-a-closure-in-c-c2y
77•ingve•3h ago

Comments

ddtaylor•2h ago
I actually enjoy trampoline functions in C a bit and it's one of the GNU extensions I use sometimes.
RossBencina•2h ago
Good to see Borland's __closure extension got a mention.

Something I've been thinking about lately is having a "state" keyword for declaring variables in a "stateful" function. This works just like "static" except instead of having a single global instance of each variable the variables are added to an automatically defined struct, whose type is available using "statetype(foo)" or some other mechanism, then you can invoke foo as with an instance of the state (in C this would be an explicit first parameter also marked with the "state" parameter.) Stateful functions are colored in the sense that if you invoke a nested stateful function its state gets added to the caller's state. This probably won't fly with separate compilation though.

juvoly•2h ago
That sounds cool, but this quickly gets complicated. Some aspects that need to be addressed:

- where does the automatically defined struct live? Data segment might work for static, but doesn't allow dynamic use. Stack will be garbage if closure outlives function context (ie. callback, future). Heap might work, but how do you prevent leaks without C++/Rust RAII?

- while a function pointer may be copied or moved, the state area probably cannot. It may contain pointers to stack object or point into itself (think Rust's pinning)

- you already mention recursion, compilation

- ...

fuhsnn•59m ago
IMO the C way is to allow users to explicitly manage context area, along the lines of posix ucontext.h or how the author's closure proposal handle closure allocation[1]. [1] https://thephd.dev/_vendor/future_cxx/papers/C%20-%20Functio...
vintagedave•55m ago
Yes, though it was a remarkably brief mention. I believe Borland tried to standardise it back in 2002 or so,* along with properties. (I was the C++Builder PM, but a decade and a half after that attempt.)

C++Builder’s entire UI system is built around __closure and it is remarkably efficient: effectively, a very neat fat pointer of object instance and method.

[*] Edit: two dates on the paper, but “bound pointer to member” and they note the connection to events too: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n13...

fuhsnn•52m ago
I dreamed up a similar idea[1] upon reading the author's closure proposal, it's also really close to async coroutines.

[1] https://github.com/ThePhD/future_cxx/issues/55#issuecomment-...

mgaunard•2h ago
I feel the results say more about the testing methodology and inlining settings than anything else.

Practically speaking all lambda options except for the one involving allocation (why would you even do that) are equivalent modulo inlining.

In particular, the caveat with the type erasure/helper variants is precisely that it prevents inlining, but given everything is in the same translation unit and isn't runtime-driven, it's still possible for the compiler to devirtualize.

I think it would be more interesting to make measurements when controlling explicitly whether inlining happens or the function type can be deduced statically.

gpderetta•1h ago
Given a Sufficiently Good™ compiler, yes, after devirtualization and heap elision all variants should generate exactly the same code. In practice is more complicated. Devirtualization needs to runs after (potentially interprocedural) constant propagation, which might be too late to take advantage of other optimization opportunities, unless the compiler keeps rerunning the optimization pipeline.

In a simple test I see that GCC has no problems completely removing the overhead of std::function_ref, but plain std::function is a huge mess.

Eventually we will get there [1], but in the meantime I prefer not to rely on devirtualization, and heap elision is more of a party trick.

edit: to compare early vs late inlining: while gcc 14 can remove one layer of function_ref, it seems that it cannot remove two layers, as apparently doesn't rerun the required passes to take advantage of the new opportunity. It has no problem of course removing an arbitrary large (but finite) layers of plain lambdas.

[1] for example 25 years ago compilers were terrible at removing abstraction overhead of the STL, today there is very little cost.

capestart•1h ago
The breakdown of lambda, blocks, and nested functions demonstrates how important implementation and ABI details are in addition to syntax. I think the standard for C should include a straightforward, first class wide function pointer along with a closure story to stop people from adding these half portable, half spooky extensions.
Progge•1h ago
Long time ago I wrote C. Could anyone fill me in why the first code snippet is arg parsing the way it is?

int main(int argc, char* argv[]) {

  if (argc > 1) {

    char\* r_loc = strchr(argv[1], 'r');

    if (r_loc != NULL) {

      ptrdiff_t r_from_start = (r_loc - argv[1]);

      if (r_from_start == 1 && argv[1][0] == '-' && strlen(r_loc) == 1) {
        in_reverse = 1;
      } 

    }

  }

  ...
}

Why not

if (argc > 1 && strcmp(argv[1], "-r") == 0) {

    in_reverse = 1;
}

for example?

tapete2•1h ago
It doesn't even make sense to use strchr for determining the position of 'r', when the code checks that the position of '-' is at index 0.

Your solution is perfectly fine. Even if you don't have access to strchr for some reason, the original snippet is really convoluted.

You could just write (strlen(argv[1]) > 1 && argv[1][0] == '-' && argv[1][0] == 'r') if you really want to.

microtherion•1h ago
It could make some sense to use strchr, because in idiomatic UNIX tools, single character command line options can be clustered. But that also means that subsequent code should not be tested for a specific position.

And if you ever find yourself actually doing command line parsing, use getopt(). It handles all the corner cases reliably, and consistent with other tools.

nesarkvechnep•1h ago
I'm thinking of using C++ for a personal project specifically for the lambdas and RAII.

I have a case where I need to create a static templated lambda to be passed to C as a pointer. Such thing is impossible in Rust, which I considered at first.

sirwhinesalot•48m ago
I think local functions (like the GNU extension) that behave like C++ byref(&) capturing lambdas makes the most sense for C.

You can call the local functions directly and get the benefits of the specialized code.

There's no way to spell out this function's type, and no way to store it anywhere. This is true of regular functions too!

To pass it around you need to use the type-erased "fat pointer" version.

I don't see how anything else makes sense for C.

nutjob2•5m ago
The price you pay for GCC nested (local) functions is an executable stack with 'trampolines'.

I'm a fan of nested functions but don't think the executable stack hack is worth it, and using a 'display' is a better solution.

See the Dragon Book or Compiler Construction: Principles and Practice (1984) by Louden

Rochus•30m ago
The benchmark demonstrates that the modern C++ "Lambda" approach (creating a unique struct with fields for captured variables) is effectively a compile-time calculated static link. Because the compiler sees the entire definition, it can flatten the "link" into direct member access, which is why it wins. The performance penalty the author sees in GCC is partly due to the OS/CPU overhead of managing executable stacks, not just code inefficiency. The author correctly identifies that C is missing a primitive that low-level languages perfected decades ago: the bound method (wide) pointer.

The most striking surprise is the magnitude of the gap between std::function and std::function_ref. It turns out std::function (the owning container) forces a "copy-by-value" semantics deeply into the recursion. In the "Man-or-Boy" test, this apparently causes an exponential explosion of copying the closure state at every recursive step. std::function_ref (the non-owning view) avoids this entirely.

gpderetta•10m ago
Even if you never copy the std::function the overhead is very large. GCC (14 at least) does not seem to be able to elide the allocation, nor inline the function itself, even if used immediately after use and the object never escapes the function. Given the opportunity, GCC seems to be able to completely remove one layer pf function_ref, but fails at two layers.
psyclobe•26m ago
c++ for the win!! finally!!

Size of Life

https://neal.fun/size-of-life/
2051•eatonphil•19h ago•222 comments

A "Frozen" Dictionary for Python

https://lwn.net/SubscriberLink/1047238/25c270b077849dc0/
19•jwilk•1h ago•0 comments

The Cost of a Closure in C

https://thephd.dev/the-cost-of-a-closure-in-c-c2y
78•ingve•3h ago•19 comments

Getting a Gemini API key is an exercise in frustration

https://ankursethi.com/blog/gemini-api-key-frustration/
561•speckx•14h ago•228 comments

Patterns.dev

https://www.patterns.dev/
298•handfuloflight•9h ago•73 comments

Australia begins enforcing world-first teen social media ban

https://www.reuters.com/legal/litigation/australia-social-media-ban-takes-effect-world-first-2025...
787•chirau•1d ago•1196 comments

Booting Linux in QEMU and Writing PID 1 in Go to Illustrate Kernel as Program

https://serversfor.dev/linux-inside-out/the-linux-kernel-is-just-a-program/
113•birdculture•6d ago•32 comments

How the Brain Parses Language

https://www.quantamagazine.org/the-polyglot-neuroscientist-resolving-how-the-brain-parses-languag...
26•mylifeandtimes•2d ago•6 comments

Auto-grading decade-old Hacker News discussions with hindsight

https://karpathy.bearblog.dev/auto-grade-hn/
453•__rito__•17h ago•205 comments

Why Startups Die

https://www.techfounderstack.com/p/why-startups-die
13•makle•3d ago•5 comments

Python Workers redux: fast cold starts, packages, and a uv-first workflow

https://blog.cloudflare.com/python-workers-advancements/
64•dom96•2d ago•15 comments

Go's escape analysis and why my function return worked

https://bonniesimon.in/blog/go-escape-analysis
11•bonniesimon•6d ago•6 comments

VCMI: An open-source engine for Heroes III

https://vcmi.eu/
109•eamag•4d ago•14 comments

How Google Maps allocates survival across London's restaurants

https://laurenleek.substack.com/p/how-google-maps-quietly-allocates
272•justincormack•2d ago•131 comments

Incomplete list of mistakes in the design of CSS

https://wiki.csswg.org/ideas/mistakes
123•OuterVale•6h ago•74 comments

Rubio stages font coup: Times New Roman ousts Calibri

https://www.reuters.com/world/us/rubio-stages-font-coup-times-new-roman-ousts-calibri-2025-12-09/
285•italophil•1d ago•479 comments

Super Mario 64 for the PS1

https://github.com/malucard/sm64-psx
233•LaserDiscMan•16h ago•91 comments

Fossils reveal anacondas have been giants for over 12 million years

https://www.cam.ac.uk/stories/twelve-million-years-of-giant-anacondas
49•ashishgupta2209•1w ago•21 comments

Qwen3-Omni-Flash-2025-12-01:a next-generation native multimodal large model

https://qwen.ai/blog?id=qwen3-omni-flash-20251201
270•pretext•19h ago•95 comments

Show HN: Wirebrowser – A JavaScript debugger with breakpoint-driven heap search

https://github.com/fcavallarin/wirebrowser
31•fcavallarin•20h ago•8 comments

Show HN: Automated license plate reader coverage in the USA

https://alpranalysis.com
182•sodality2•17h ago•105 comments

Flow Where You Want – Guidance for Flow Models

https://drscotthawley.github.io/blog/posts/FlowWhereYouWant.html
19•rundigen12•5d ago•1 comments

McDonald's removes AI-generated ad after backlash

https://www.theguardian.com/business/2025/dec/11/mcdonalds-removes-ai-generated-christmas-ad-adve...
7•terabytest•22m ago•1 comments

Common Lisp, ASDF, and Quicklisp: packaging explained

https://cdegroot.com/programming/commonlisp/2025/11/26/cl-ql-asdf.html
84•todsacerdoti•1d ago•20 comments

Scientists create ultra fast memory using light

https://www.isi.edu/news/81186/scientists-create-ultra-fast-memory-using-light/
99•giuliomagnifico•6d ago•24 comments

Valve: HDMI Forum Continues to Block HDMI 2.1 for Linux

https://www.heise.de/en/news/Valve-HDMI-Forum-Continues-to-Block-HDMI-2-1-for-Linux-11107440.html
740•OsrsNeedsf2P•17h ago•411 comments

3D-printed carotid artery-on-chips for personalized thrombosis investigation

https://advanced.onlinelibrary.wiley.com/doi/10.1002/adma.202508890
20•PaulHoule•1w ago•2 comments

Terrain Diffusion: A Diffusion-Based Successor to Perlin Noise

https://arxiv.org/abs/2512.08309
129•kelseyfrog•16h ago•38 comments

Gundam is just the same as Jane Austen but happens to include giant mech suits

https://eli.li/gundam-is-just-the-same-as-jane-austen-but-happens-to-include-giant-mech-suits
218•surprisetalk•1w ago•147 comments

Is it a bubble?

https://www.oaktreecapital.com/insights/memo/is-it-a-bubble
244•saigrandhi•17h ago•378 comments