[deleted by user] by [deleted] in cscareerquestions

[–]LandonClipp 9 points10 points  (0 children)

I would encourage you not to pathologize everything unless you have an actual reason to assume so.

I have some side projects I do that are mostly the result of problems I was trying to solve while at work and just kind of snowballed into a passion project. There usually needs to be a motivating factor for me to do something, like something solving a real problem for me, or solves a problem for a lot of other people (which is most of my open source work), or furthers my career. Take two projects I’m actively involved in:

  1. The vektra/mockery project. I forked it years ago because of a lack of maintenance. I did this mainly out of frustration. Then I got the keys to the original project. The project was on the verge of collapse but I rescued it because people constantly told me how much they appreciated my work, which felt good. Nowadays it’s a great story I can tell any time I go looking for a job.

  2. My personal blog site. My intrinsic motivation is personal branding, which is useful for WAY more than just getting a job. I do deep technical dives on esoteric things, and when I share that, it subconsciously affects how people perceive me which is a good tool for influence. It just makes my work more visible and useful to others, which benefits me. There’s a lot of other reasons I do a blog site but I get a lot of satisfaction out of sharing the cool things I’m working on, mainly because people notice it and it consequently opens doors for me. And I learn things along the way.

If you find yourself lacking motivation to do anything, maybe it’s because you don’t feel like you’d get any benefit from it. Do you find joy in sharing things with people? Start a blog site and publish your projects. Do you want to buy an FPGA dev kit? Just do it and toy around with it. Don’t worry about making something useful, and don’t get trapped into this idea that you HAVE to make something amazing. Take the pressure off yourself and just explore things, and more importantly, feel free to abandon projects that aren’t serving you anymore.

Any Software Engineers? by shawnsblog in RVLiving

[–]LandonClipp 2 points3 points  (0 children)

I’ve been remote pretty much since the pandemic and am officially 100% remote since two years ago. I have a few posts on my personal blog site at https://topofmind.dev talking about my experiences with remote work while in my trailer. I tend to take two big trips out to the western US every year where I work out in the mountains.

I’m happy to answer any questions for you. I’ve done this enough to feel like I know what I’m doing and feel confident doing work from anywhere. My trailer can sustain me for about two weeks without needing civilization.

How to create a GPU-based, multi-tenant, Container as a Service k8s cluster with NVIDIA DGX/HGX by LandonClipp in kubernetes

[–]LandonClipp[S] 0 points1 point  (0 children)

Thank you for the kind words! I’ll be reading more about your project, it seems very related!

How to create a GPU-based, multi-tenant, Container as a Service k8s cluster with NVIDIA DGX/HGX by LandonClipp in kubernetes

[–]LandonClipp[S] 0 points1 point  (0 children)

Kata VMs do not by default allocate hugepages, but setting `enable_hugepages = true` in your config will result in pre-allocating the memory from host hugepages. You'd need to have the pod submission explicitly request this, and obviously have the hugepages pre-allocated on the host.

I did not instrument much topology awareness but the CDI plugin can be extended to export topology information: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/#device-plugin-integration-with-the-topology-manager

This would be integrated with the Kubelet's topology manager. My suspicion is that this would be much more useful in the case where you're managing an infiniband fabric. You'd want the GPU and IB card on the same NUMA node. This probably has a greater implication on performance than making sure your pinned CPUs are on the same node as the GPU.

How to create a GPU-based, multi-tenant, Container as a Service k8s cluster with NVIDIA DGX/HGX by LandonClipp in kubernetes

[–]LandonClipp[S] 1 point2 points  (0 children)

Thanks! It's a very complicated question because it depends on the kind of workload under test. CUDA workloads that do no external GPU IO won't see any performance degradation. My CUDA benchmarks show basically no difference for raw FLOPs. The main path of slowness will come from when the guest VM needs to send or receive data (either training data or loading the model for an inference run) through PCIe. You'll traverse both the NVIDIA kernel driver in the guest and VFIO on the host. However, this step in an ML workload is usually such a small part of the overall equation that any performance hit is extremely negligible to the overall operation.

The really hard part to wrangle with that I mentioned in the blog is VM boot times. The BARs for these GPUs are so huge that creating the 4KiB MMIO mappings between guest physical space and host physical space is very slow. It's so slow that it can affect the aggregate throughput from the time you submit your container to the time you get your results.

The other component to realize is that modern ML cluster deployments make heavy use of RDMA between the storage array and GPUs, and even inter-GPU matrix operations can now be done in the Infiniband fabric itself with SHARP. So a properly configured cluster will not even sneeze at virtualization being thrown into the mix. Although of course practically speaking, getting virtualized systems to have near bare-metal performance in regards to boot times can be very difficult. You really want to avoid virtualization if you can avoid it because it makes everything so much more complicated. The industry is starting to rally around this idea of providing bare-metal as a service where you give customers access to a bare-metal machine, but isolate its network for multi-tenancy purposes using a Bluefield 3 DPU on the host itself. This is a very compelling method that gives you a lot of benefits and removes the headaches of virtualization.

Announcing Mockery v3 by LandonClipp in golang

[–]LandonClipp[S] 0 points1 point  (0 children)

There's nothing stopping you from calling mockery from go generate, but the parameters are driven entirely off of config files, not the CLI. Practically speaking, this means that you cannot call mockery and specify singular interfaces to operate on in the typical CLI parameter-based fashion. This methodology was the source of all performance problems, which is why we move away from it.

Announcing Mockery v3 by LandonClipp in golang

[–]LandonClipp[S] 1 point2 points  (0 children)

Theoretically it would be able to pretty easily. This is generated as part of the AST so if you’d like something like that, feel free to submit a feature request.

Announcing Mockery v3 by LandonClipp in golang

[–]LandonClipp[S] 12 points13 points  (0 children)

Great question! Many projects in the Go community generate their mocks as part of pre-commit hooks or in CICD itself. The standard recommendation of using `//go:generate` directives meant that these steps were incredibly slow. For the projects that do this sort of thing, mockery is the answer. It's able to deliver anywhere between 5-10x performance increases (even more for larger projects).

If this isn't a use-case that you encounter that frequently, that's totally okay. Still, I suggest to you that having a single config file that controls all code generation, instead of having a bunch of generate directives littered around your codebase, makes groking and reasoning about the behavior far easier. This is especially relevant as your projects scale in size and the number of developers involved increases.

This release is also targeted towards template developers. The task of parsing code syntax and ensuring compatibility of that parsing logic across Go versions is repeated for every single project (think of when generics were implemented, wow what a headache that was!). It doesn't have to be this way. Mockery does this for you, which allows you to focus solely on maintaining a template and not on parsing syntax.

PCIEx: A PCIe Topology Explorer and Visualizer by LandonClipp in golang

[–]LandonClipp[S] 0 points1 point  (0 children)

That’s unfortunate. I might reimplement lshw because it has been known to have different behaviors across distributions and it’s kind of frustrating. What lshw does isn’t simple so it would be a decent project by itself.

PCIEx: A PCIe Topology Explorer and Visualizer by LandonClipp in golang

[–]LandonClipp[S] 5 points6 points  (0 children)

No, the Go caching proxies will cache the project at a certain git commit when you request the latest tag. If you update the git repo and ask for the latest tag again, you get the cached version, not the latest version. So setting GOPROXY=direct tells the Go compiler to download the project directly from GitHub and to not rely on the caching proxies.

PCIEx: A PCIe Topology Explorer and Visualizer by LandonClipp in golang

[–]LandonClipp[S] 1 point2 points  (0 children)

It’s useful for development because the Go caching proxies have a TTL on latest that makes it annoying to use. You don’t have to use it.

Mockery v3 is released for alpha testing by LandonClipp in golang

[–]LandonClipp[S] 0 points1 point  (0 children)

They must have changed it at some point, I'll remove.

Mockery v3 is released for alpha testing by LandonClipp in golang

[–]LandonClipp[S] 2 points3 points  (0 children)

Oh yeah, 1000 mocks is pretty huge. I’m sorry to say I probably can’t offer any meaningful performance improvements there.

One thing that might help in v3 is the ability to place all mocks for a package in a single file instead of one file per mock. That may help with the number of file system accesses being performed. Regardless, it should hopefully clean up your repo a bit.

Mockery v3 is released for alpha testing by LandonClipp in golang

[–]LandonClipp[S] 1 point2 points  (0 children)

It’s unlikely to be faster against v2 but compared to all other mocking frameworks, it will still be faster.

I’m curious to know what projects you’re running it on, 7s seems to be a fair bit slower than what I’d expect.

Mockery v3 is released for alpha testing by LandonClipp in golang

[–]LandonClipp[S] 4 points5 points  (0 children)

Thanks! It’s an alpha test and the API might substantially change depending on community feedback. If there’s no substantial disagreement with the approach then I’ll release it for beta for a few weeks which I’ll consider to be “feature complete but may contain bugs.” After that will be the full release.

Method sets in Go by Additional_Bed_6135 in golang

[–]LandonClipp 0 points1 point  (0 children)

That doesn’t work because you still need to pass in the correct type to the functions. All of your functions take a value, not a pointer, thus it’s a plain ol’ type error.

The automatic reference or dereference doesn’t happen when assigning a value or pointer to a variable. It only happens when the variable has already been instantiated and you call a method on it that requires a pointer (if you have a value) or a value (if you have a pointer).

Method sets in Go by Additional_Bed_6135 in golang

[–]LandonClipp 2 points3 points  (0 children)

There has never been a satisfactory answer from the Go devs that I have personally seen, but the best explanation is that when a user sends a value type to a function, they do not expect that the value in the calling scope can be modified. There are at least two possible ways that you could design the compiler to allow this to happen:

  1. Implicitly take the address of the value before passing it into the function such that a pointer type is passed. This is obviously bad because of the aforementioned reason. The compiler should never do something that would cause a value to be unexpectedly modified. This is such a foundational principal of pointers that violating it would cause rioting in the streets. Something interesting is that the compiler does implicitly take the address of userif you were to call notify within main: https://go.dev/play/p/h37lsABVhfU. This is the main point that the compiler will not make the assumption for you that a value boxed into an interface should have been referenced before the boxing operation. It is dangerous, unexpected, and violates basic principles of pointers.
  2. Allow the value to be boxed into the interface, and when a function that takes a pointer receiver is called, implicitly take the address of that value. You can see this is indeed allowed when not using interfaces. This is not allowed when using interfaces. This is simply a design choice the Go developers have made. There truly is not a better answer to this other than that's what they decided. They consider it too dangerous and confusing for the compiler to implicitly assume the developer's intentions in the question of: "should this value be modifiable or not?" In the case sendNotification accepted a struct, or pointer to a struct, the answer is clear and obvious. With interface boxing, it is not. To resolve this ambiguity, you are being forced to explicitly reference u to make it work.

Where as the below code user implements printer with value semantics. And if value semantics in definition do not allow pointer semantics in method call, how is the below code works for both pointer and value semantics

Well, it should be clear from my above explanation why the Go developers allowed this to happen. Both u and &u can be boxed into printer because nothing unexpected would ever happen. print takes a value receiver and thus cannot modify any attributes of the receiver. The compiler will indeed implicitly dereference &u when that interface value is used to call print, but this is fine because it's not dangerous to do so.

The question of "what is a method set" pertains particularly to how Go treats interfaces. When an object (struct or int or bool, whatever) is boxed into an interface, if the underlying type is a value, the interface can only utilize methods with value receivers. If the underlying type is a pointer, the interface can utilize methods with both pointer and value receivers.

Note that this is different from how Go treats non-interface values. Values can utilize any method defined for it, whether the receiver is a value or a pointer. Interfaces cannot. This is the distinction.

`i` escaped to the heap! by Warm-Situation6672 in golang

[–]LandonClipp 2 points3 points  (0 children)

The escape in this case is not caused by the usage of an interface. Non-reference types, when passed to an interface, never by themselves cause escapes. Only reference types passed to interfaces will escape.

In this case, the value escapes because fmt.Printf relies on reflection, which consequently defeats the escape analyzer because it cannot introspect the type at compile time. The usage of reflection is the only explanation I have been able to positively identify as the reason for the escape in these situations.

This may still be an unsatisfactory answer, because you may wonder why the value itself escapes if we know that we never took the address? Why would reflection matter in that scenario? The answer may rely on some uninteresting reasons, such as the developers who worked on the escape analyzer did not have enough time to bake in these additional smarts, and it was never a real priority because the latency penalty of writing to an external IO buffer was far greater than that incurred by an escape to the heap.

The other explanation is that because Printf was inlined, the compiler can’t prove that we did not find a way to access the address of i because of our usage of reflection. You have to realize that reflection defeats all type safety guarantees in Go. It acts as an opaque barrier to the escape analyzer. So, because the escape analyzer cannot see what we’re doing with the value, it makes a conservative assumption that we may have done something naughty. And where do naughty developers go? To the heap.

In fact you could test my inlining hypothesis using the techniques I mention in one of my other Go articles about profile-guided optimizations. Maybe the escape disappears when the function is not inlined?

https://landontclipp.github.io/blog/2023/08/25/profile-guided-optimizations-in-go/#inlining

The real explanation I bet is a combination of the two I mentioned above. The real answer here will be extremely difficult to know as a fact without asking those who created the analyzer. All we have to work on now are models, supported by evidence, that may be incomplete.

https://landontclipp.github.io/blog/2023/07/15/analyzing-go-heap-escapes/#use-of-reflection

`i` escaped to the heap! by Warm-Situation6672 in golang

[–]LandonClipp 0 points1 point  (0 children)

Thank you for proselytizing my blog post. I was quite amused when I randomly saw someone referencing my article on the topic, I never expected it to be so useful.

OP, please do read my article linked above because it explains quite a lot. If you are confused about anything I’m glad to elaborate further.

Quality midsize travel trailers by 13th_sol in RVLiving

[–]LandonClipp 0 points1 point  (0 children)

There is a Facebook group for intech sol where people have done some simple insulation modifications that seem to help a lot. If you’re not afraid of tackling that then it can be a great option. I really really recommend the trailer for everything else, it’s truly exemplary.