Fork me on GitHub
Mark Wardle11:02:23

Does adding implementations of interfaces to records have any appreciable performance or memory consequences?

Mark Wardle11:02:10

My reading suggests not, as interfaces should be recorded against a class and not each instance. And my testing suggests not as well, but I don't want to miss something obvious. Thank you.

Ben Sless11:02:00

I think that there's slight overhead incurred by the class being larger, so allocating and later loading it in cache from memory will be slightly slower. Based on some benchmarks I ran couple of years ago

👍 4
Mark Wardle11:02:39

Thanks Ben. It’s only because I want to make records consumable by my Java applications, and type hints work for primitives but casting Strings etc gets frustrating. Perhaps I will keep my Clojure side clean and wrap on the Java side instead. Thanks again.

Ben Sless11:02:12

The overhead I found was tiny, so I wouldn't worry about it from that angle

👍 2
Mark Wardle14:02:17

Got it. I wouldn’t care about an overhead for a class only, but I would if it affected handling millions of instances.


I don't think it should be worse than implementing an interface on any other Java class, but I'm not sure.

Mark Wardle07:02:25

Thanks. And when I look at decompiled byte code, records have so many interfaces anyway IRecord etc… that one more probably won’t matter in any event. That said, it does feel redundant to have to implement an interface simply to get type information. It’s a shame the public instance fields don’t respect the type hints but I can understand why they are just Object instead.

Mark Wardle11:02:50

Is there a standard way of recording the results of benchmarking from e.g. criterium, so that one can track regressions/improvements from version to version, notwithstanding the difficulties in consistent benchmarking across machines etc.

Mark Wardle11:02:58

I find myself doing a lot at the REPL, and then rather than moving that code into someplace I can keep and re-run, just like tests, I throw away. Is there a better approach?


there are macroses that returns result instead of printing it. if I'm not mistaken they are benchmark and quick-benchmark

Mark Wardle13:02:10

Thanks. Yes that’s what I use at the moment but i was thinking more about the running a number of benchmarks, collecting all of the results and tabulating / charting / looking across from commit to commit.


I assume you’re looking into something more integrated into the dev lifecycle, but we put lots of timing info into Grafana, even down to the function level where we really care. You could groups metrics by release etc if you wanted.


It’s interesting actually because all our ML people have workflows where they constantly track model eval metrics and can trace everything to exact training datasets and model versions. If performance matters there’s probably stuff to learn from MLFlow generally.

Mark Wardle20:02:06

Thanks Thom. Yes that sounds more like what I was looking for - a way to turn benchmarks into a benchmark suite that can be run and check I’ve not caused a major speed regression / storing the results of output from criterium for example. Tracking metrics.


You can just use a test, that runs quick-bench of some code, and assert it's below whatever time you want it to remain below.

👍 2
Mark Wardle07:02:39

Thanks @U0K064KQV - I’ll do that and also capture the output and write something that spits out the output from all into a file for later perusal using a test set up and a dynamic var. I guess I thought that was boilerplate that might have been solved elsewhere but it is likely to be so specific to use cases, all the hard work then done by criterium and Clojure.test.


How often do you use drefrecord and when you use / not use them? (I don’t consider Datomic here) Just thinking if I should use them more often or there is no point to do it. Why I even think about it? Very often I found myself transform data from third party system into inner data structure and it can be defrecord or map.


I use them only in two situations: 1. interop with the JVM (although that doesn't always work due to how some java libs work, but it gets the job done) 2. Component (or more broadly where I need things to conform to an interface and be swappable)

👍 6

Re: Component -- the Lifecycle can be supplied via metadata so you can use any Clojure object that can carry metadata. If you need dependencies, a Component must be Associative by name, but can still be a plain hash map (with metadata if you need start/`stop`). For example, next.jdbc provides Component compatibility without depending on Component, via metadata, and uses a plain hash map for one state and a fn for the other state:


(I wouldn't do this on a performance-sensitive pathway since metadata protocol implementation is slower than using types, but it can be very convenient for test setup and mocking etc)


TL;DR: I hardly ever use defrecord because plain hash maps do (almost) everything I need in (almost) every situation.

👍 6

glad to hear that, I was worry only I don’t use defrecord 🙂


Yeah, they don't get much use usually - most of the code I wrote that uses them are basically things to hold on to a connection of some sort and close it when you don't need it anymore.


Same, don't use them too often. Though I think they can be appropriately used for your main entities. It's a nice way to document the shape of them, if you don't want to go all the way to spec or Malli. The downside is they can't properly tell you the type or the optionality of any fields. Which is why I still most often use maps+spec


I use at times huge maps converted from svg; on evaluation I’ll get an ‘uneven number of forms’ error; is there a way to lint it or find the missing value without having to look through it by hand? Emacs/Cider user here if that matters.


the exception should give you a line and col number


I thought I remembered a similar question not too long ago, Did the advice from that thread not help here?


Wow… I am a bear of little brain! I completely forgot I had asked and, apparently, the help. Unbelievable. Thank you for remembering. Just a little embarrassed! I’m going to delete this new iteration to save folks and the channel the clutter.


It's better not to delete the OP but rather mark it as answered (e.g. with a reaction or by editing the message and striking through the whole text and adding something like "never mind"). Removing the original post when there's already an active thread only creates more confusion.

👍 4

Good point, and I’ll never do it again… I had thought the whole thread would go away… nope.


Well, even with the whole thread gone it would be confusing to the people that are already participating in the thread. :) Not a big deal in this case though. But sometimes some people remove OPs and their thread messages after there have been 20+ messages in the thread from 5+ people.

👍 4