IRC channel logs

2026-07-05.log

back to list of logs

<old>I wonder, is there a way for define-record-type to be only done once
<old>say I reload the module
<old>the new definition is imcompatible with the old definition
<old>or maybe rnrs records could help here? Same type definition merging something like that
<czan>I'm trying to iterate over a hash table (made with (make-hash-table)) using hash-for-each, but I want to exit early for performance reasons. The only options I can think of are using call-with-escape-continuation, or throwing an error. From my initial test call-with-escape-continuation is slower than just iterating the whole table, and using an error to do this seems like the wrong thing to do. Is there another opti
<czan>on I've missed?
<JohnCowan>czan: Ids thesze of the hash table fixed?
<czan>Not at all. The more specific thing is iterating through one hash table and comparing keys to matching keys in a second table using a custom comparator.
<czan>The hash tables are also pretty large (they're counts of instruction pointers that I've accumulated using Guile's trace hooks).
<czan>Looking at hashtab.c, it seems hash-for-each is in C, which is hard for me to work with. If I could access the underlying vector then I could do what I need, but I can't see a safe way to do so (for good reason, I guess). Then it looks like srfi-69 and the rnrs code just call through to the same C code eventually. Alas.
<JohnCowan>As the table grows, then, the fixed overhead of call/ecshoud become less important. But you should ask yourself if you should double up on the data structure, keeping a hash table for lookup and a list or vector for traversal, trading space for speed.
<czan>The problem is that I need to match up the keys from two different traces, which might have hit different instruction pointers. So I don't have a consistent keyset between two tables that I'm comparing. I actually don't really care about lookup, except for the fact that I want pairwise comparison by matching keys.
<czan>If there's a better structure already implemented, I'd happily use that. Otherwise I might have to implement my own hash structure which exposes the underlying vector, or something.
<czan>Actually, I guess I could use a sorted vector, and then do the comparisons by walking both of them in sequence. That would be much more straightforward. Then I just have a O(nlogn) cost when constructing the vector (which is fine), to give me more straightforward O(n) comparisons.
<czan>I'll give that a go some time soon and see how I go. Thanks!
<ArneBab>czan: that sounds like a usecase for set operations: first get the intersection of the keys, then iterate over this intersection of keys (retrieving only those from the hash-maps). But performance of set-operations might be an issue (srfi-1 isn’t the fastest).
<czan>Yeah, but I also want to avoid allocation during these comparisons. I do a lot of them, so ideally want I want to not generate any garbage, and come up with an answer as fast as possible.
<ArneBab>That’s a good point
<ArneBab>I’ve seen what dthompson got in speed by avoiding allocations
<ArneBab>czan: did you see https://aartaka.me/guile-optimization.html ? Maybe it could give some additional tips.
<ArneBab>czan: I’d love to see a short writeup of what you ended up with!
<czan>I did see that just today, actually. I'll see how I go - I'm hoping my writing energy might go towards the writeup for the library in general, rather than this specific optimisation, but we'll see what I can get working. :)
<ArneBab>Thank you!
<old>czan: what kind of instrumentation you are trying to do?
<czan>As in, what's my overarching problem where I'm using these traces? Or are you asking what I'm measuring to try to optimise this specific thing?
<old>what are you trying to measure in general
<old>I work on user-space tracing (LTTng) so I have lots of insight in tracing in general
<czan>I'm writing a property based testing library with coverage-guided mutation. So I run the property on a random input, then use the trace information to decide whether this was an "interesting" input. I then keep a list of the most interesting inputs and construct new inputs by making changes to them.
<old>so you need to know which branches were take I suppose to determine if an input was interesting and keep it for mutation
<czan>Roughly like this paper, but with substantial changes due to being in a dynamically typed language: https://www.mista.me/assets/pdf/icst23-preprint.pdf
<czan>Yeah, unfortunately I found it hard to get good branch information, so I'm just using instruction pointer counts as a sparse vector.
<old>hm
<czan>Then treating "interesting" as whether it's < in the lattice they form.
<czan>It's a bit crude, but it's a start. :)
<old>pointer count as in you increment every IP you encounter?
<czan>Yep. I basically stole the code from Guile's coverage stuff, I just don't resolve things to filenames/lines.
<old>which means, for every instruction you do: mark that IP, execute at IP, move to next IP ?
<old>I see. Hm
<czan>Specifically I gather the counts with this: https://git.sr.ht/~czan/ferret/tree/main/item/src/ferret/coverage.scm#L35-58
<old>ohh that's going to be very slow indeed
<czan>Yeah, but that's not the bit I'm worried about. That is slow, but that's "fine" because I'm making the mechanism pluggable. If you want a different notion of coverage that's fine, as long as it produces a hash-table in the end.
<czan>The idea is that the native-coverage one is general, but you can use call-with-counter-coverage (earlier in the file) if you want to do your own instrumenting.
<czan>But either way, when I run it under Guile's profiling I see the "compare two traces" function as being pretty expensive, which is what I was trying to solve earlier.
<czan>Part of that expense is just that it's called a lot (every run has to check whether it's interesting against all currently-interesting traces), so my current thought is that I need to find a way to call it less rather than making it faster.
<old>it is slow because you instrument way too much IPs
<old>if you can only instrument certain type of instruction instead
<old>but in general the tracing mechanism in Guile could be improve
<czan>I have found it very hard to find out any information about the instructions themselves. If you can give me an idea of how to know which instruction is being executed that would help me. :)
<czan>My ideal would be to know "this is a branch instruction" and whether it branches, or something like that. I think branches are the most interesting thing for me to trace.
<old>yes you need to only instrument conditional branching instruction and you can produce a single bit: taken or not
<czan>Yeah, I just don't know how to get that information out of Guile. :)
<old>well, I can certainly think of one way to hack this, but you would need to disable the JIT for it to work
<czan>I already do that by using the debug VM, don't I?
<old>hmm that I don't know for sure
<old>I suppose it makes sens yes
<old>basically, you can patch the VM itself using dynamic instrumentation in C
<old>you inject a hook in the VM at certainl specific location
<old>but that require writing some C
<old>but the idea is, if you can inject a trampoline in the VM for the instruction `je' for example
<czan>I'm open to writing some C (although I'd like to avoid it so I don't need autotools or whatever), as long as I don't need to distribute a modified Guile.
<old>you can in a C callback, before the VM execute, determine which branch is going to be taken
<old>and you emit a pair: (IP, 0|1)
<old>with this, you can rebuild the whole control flow of a program
<old>now to inject something in the VM at runtime, this is called dynamic instrumentation
<old>GDB has fast tracepoint that allow you to inject probe like that are very fast
<old>but maybe all of that can be done in the debug engine in Scheme, I would have to check
<czan>With dynamic instrumentation, are you talking about a Guile feature? Or doing something outside of Guile? Ideally I want my code to be usable as a regular Scheme library in Guile (i.e. just use-module and start writing a test).
<old>the goal of dynamic insrumentaiton is to avoid any recompilation
<old>as opposed to static instrumentation
<old>I will try to hack you something
<old>maybe later today, I have a chicken coop to finish :-)
<czan>No worries. If you're able to put something together I would really appreciate it, but there's no really hurry on it. This is just for fun, so I have no deadlines whatsoever. :)
<old>the difficult thing is finding where to inject the dynamic instrumentation
<old>the VM is a big dispatch function so there's no symbol in it
<old>one thing we could do in Guile to ease that is to add labels that are exported in the debug symbol table
<old>these cost nothing and the compiler will emit them at the correction location where we want our insturmentation to be done
<old>will look into that
<haugh>Why does set-field throw "unknown getter" against an exception? Short of destructuring the record with match or match-record, is there a straightforward way to (functionally) edit a single field of an exception?
<haugh>Please excuse me if I've been spamming; had some troubles with my IRC
<haugh>%set-field is totally incomprehensible to me and afaict completely undocumented.
<haugh>I'm using (ice-9 exceptions). Also, am I right in my understanding that there is basically no built-in facility for handling multiple instances of an exception type in a compound exception?
<haugh>Also, is there any way to detect whether an exception was raised continuably? I'm aware I can tag it with an instance of &continuable
<haugh>*retroactively detect
<haugh>it seems that ice-9 exceptions are not immutable records, but they do have immutable fields.
<rlb>Offhand, I don't think exceptions/conditions support mutation.
<rlb>And I imagine you've seen that guile's newer exceptions are related to https://r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-8.html#node_chap_7
<haugh>okay, but why doesn't (srfi srfi-9 gnu) support ice-9 exceptions?
<rlb>Hmm, what do you mean by support?
<rlb>Also don't know if it helps, but looks like new-style exceptions are actually records: https://codeberg.org/guile/guile/src/commit/fd6ef7c32958000cf1b3bcc73ab0bf40c276eff5/module/ice-9/boot-9.scm#L1457 But I'm not sure that's promised anywhere, and/or should be assumed.
<haugh>(set-field (make-exception-with-message "foo") (exception-message) "bar") throws "unknown getter" on guile 3.0.9. Apologies for the older guile, something wrong with my guix profile atm.
<haugh>they certainly are records, and I can destructure them with (ice-9 match)
<haugh>but what I need to do is modify a syntax error that's part of a compound exception and right now my solution is to iterate over the components field of the compound exception, copy the syntax-error-form I want, and instantiate a new exception with all the old components plus a new syntax-error
<haugh>and it occurs to me I'm writing loops on top of an extremely high-level interface
<rlb>I'm not sure I've ever used set-field.
<rlb>Also, I'm not sure exception-message is the "accessor" for the record.
<rlb>e.g. exception-message and (record-accessor &message 'message) aren't the same --- ice-9/exceptions.scm does not directly call define-record-type when defining the exception records.
<rlb>It may be that that your higher level approach si the only "reliable" thing to do. i.e. unless guile makes enough promises about "exceptions" that you can count on it not (justifiably) changing them in a future release.
<haugh>that is correct, but set-field doesn't appear to eval that form, so I can't pass the result of record-accessor
<haugh>yes I'm concerned that you're right about the interface
<rlb>It might also be that we want to eventually provide some supported way to do what you're doing, i.e. maybe we do eventually want to say that set-field will always work on new-style exceptions, or provide some exception-related (ice-9 exceptions) equivalent.
<haugh>well it's been seven years so
<rlb>But I haven't used &exceptions heavily yet, so I may be missing something.
<haugh>I have to say that if this system was designed for a net decrease in cortisol then it is not currently passing
<haugh>rlb, thank you for the feedback, i feel a little less crazy
<rlb>Certainly --- and tangentially related https://codeberg.org/rlb/guile/commit/c6196899d51e2c7bb3dba201d483725e43a9f937 :)
<rlb>I noticed we were just dropping the actual errno when converting to rnrs exceptions in cases where you might really want to see the original value.
<haugh>I'm no polyglot but it seems that exceptions are a common achilles' heel for languages across paradigms
<rlb>I don't think it's often one of the easier bits :)
<rlb>and more broadly "error handling"
<rlb>And though I haven't thought hard about it yet, seems possible that arguments similar to those in favor of set-field(s) could apply to exceptions too.
<haugh>in my extremely naive opinion, compound exceptions should either explicitly support or forbid multiple instances of the same exception type as components. As of 3.0.9 you can get an incorrect response.
<haugh>But to say something nice, having continuability and unwinding scope as independent and interoperable features is extremely powerful.
<rlb>You mean wrt multiple &messages? I can't recall whether rnrs wanted to allow that.
<rlb>(or said anything at all)
<haugh>I have multiple syntax errors I'm trying to combine but yes that's the idea
<identity>rlb: pretty sure R⁶RS says nothing about that
<rlb>haugh: not sure it helps, but wrt combining, guile main now has srfi-235 (e.g. group-by).
<rlb>i.e. if you were trying to aggregate the content of all of one of the types or something.
<rlb>(and if it works the way I suspect it does)
<haugh>it does work like that, incidentally the lib I'm adding exception handling to evolved out of my own 235 implementation. that srfi is still under discussion wrt conjoin/disjoin so I wonder how that will work its way into guile. the srfi also does not support multi-value which was my initial motivation
<haugh>happy to hear it's added!
<rlb>Interesting. I don't actually know much about that srfi yet, just recalled it had been added.
<rlb>(Also recently added: 197 and 207.)
<rlb>(and 119)