Hacker Newsnew | past | comments | ask | show | jobs | submit | coreytabaka's commentslogin

No, that's based on the deprecated multi-level round-robin scheduler, which we find tremendously amusing. :)


Oh gosh well thank you for pointing that out


Who's "we"?


It does imply that. Multiple concurrent scheduling algorithms is nothing new, Linux and MacOS both support per-thread algorithm selection.


What does the super scheduler that coordinates the two schedulers approximately look like?


RT tasks get prioritized over normal ones but only up to a configurable fraction of CPU time slices.

If you need even more guarantees than that another option is to pin tasks to a set of CPU cores to isolate workloads from each other. The kernel can also be told to not use certain cores for interrupt handling or kernel-internal tasks. So with some effort it's possible to almost entirely dedicate a core to a single thread.


A simple hierarchy: when there is deadline work to do that work takes precedence, fair work gets the rest of the time. This is effective because deadline work has bounded execution time, whereas fair work is elastic and can adjust to use the available bandwidth.


Where is io scheduling in this?

It's my perception that on current workstations, for example, that and "ionice -c3" in front of any build I do is far more useful then simply nicing it.


IO scheduling is a separate problem, though it shares the same fundamental properties. The same goes for network packet scheduling. All of these are ongoing efforts. Stay tuned! :)


Well I look forward to seeing this develop.

Two of my primary areas of interest, audio engineering and VR both REALLY show how poorly modern operating systems do with user interactivity when it counts.


The table approach is fairly flexible. Both Protos and FlatBuffers do more or less functionally similar things. What specific deficiency do you see?

Take a look at the binary format spec:

https://github.com/google/libnop/blob/master/docs/format.md

The format is designed specifically to provide enough structural information that the binary format can be parsed without knowing the original structure definitions. This makes it easy to write a binary-to-string converter than can grok any payload with minimal complexity. Message observability was a primary goal during development.


Proto/Flatbuffers are okay for message passing, but many times you don't want to pass messages, you want to serialize C++ structures. There's no way to represent your 1Gb in-memory hashtable as a Flatbuffer, and that is not okay.

Writing a binary-to-string converter sucks. You write code once, and debug it every day. Anything that makes debugging harder is humongous pain that shouldn't exist; binary-to-string converters can be written, but let's be real for a moment: this is something that is dead last on the list of business priorities. If your message can be parsed with ad-hoc shell or Python scripts, that's a huge win for everyone.


Yes! Hopefully C++20 or C++25-ish will provide better alternatives for compile-time reflection.

Don't forget that libnop also has NOP_EXTERNAL_STRUCTURE (and friends) to decouple the annotation from the structure definition. This is handy when you have a C ABI with C++ internal implementation. I don't recall seeing a similar facility in other libraries.


Most of the above macros work this way. Boost fusions macro will let you specify arbitrary code for getting and setting attributes.


Thanks! MessagePack is one of the influences.


This argument comes up a lot within Google. The style guide is revisited frequently (for example =delete in the public section is now the standard for disabling copy and/or move/assignment vs. the old macros in the private section).

Non-nullable types are helpful for implying pre-conditions. However, readability at the call site (e.g. Foo(&bar) might mutate bar, whereas Foo(bar) should not) is still considered more valuable in a large scale codebase. Passing nullptr as a pointer argument is generally assumed to not be okay unless explicitly documented as permitted -- this is opposite the assumption that you stated.

There are places exceptions are made, where the use of pointers is deemed more confusing than non-const references (e.g. move-maybe semantics in very specific cases). Ultimately, most code follows the default style guide.

Besides, nullptr dereferences are pretty easy to diagnose in a library like this. And more often than not everywhere else too.


> However, readability at the call site (e.g. Foo(&bar) might mutate bar, whereas Foo(bar) should not) is still considered more valuable in a large scale codebase

but does it make it any more readable ? if you use the pointer anywhere else and have it as a variable then suddenly you don't distinguish anymore between a pointer and a reference. It would frankly make more sense to have empty `#define in` and `#define out` macros and make a small clang plug-in that checks correct usage in your codebase - e.g.

    int foo(int x, const foobar& my_foobar, boo& my_boo);
    foo(x, in fb, out b); // ok
    foo(x, out fb, out b); // compile error


In most cases the context provides enough information:

  void Baz(Bar*);
  void Baz(const Bar&);

  void Foo(Bar* mutable_bar) {
    Baz(*mutable_bar); // Not mutated.
  }

  void Foo(Bar* mutable_bar) {
    Baz(mutable_bar); // Possibly mutated.
  }

  void Foo(const Bar& bar) {
    Baz(bar); // Not mutated.
  }

  void Foo(const Bar& bar) {
    Baz(&bar); // Compiler error.
  }
The rule doesn't perfectly eliminate ambiguity, but it does a pretty good job overall. The point is to address the general use cases with familiar constructs that work across multiple toolchains.


unique_ptr and nop::Optional<unique_ptr<T>> is on the way. shared_ptr opens up the ability to create cycles, which are not supported yet.


It means not currently an officially supported project. Most open source releases of internal code start out this way and may or may not become officially supported. It is different than deprecated, which was once supported but no longer.


Primary author and maintainer here! Happy to answer any questions anyone has.


Have you done any speed comparisons to other libraries? Since there are so many of these libraries that is a big deciding point, especially in C++.


I'm considering using the library for an embedded project that communicates with a Linux host and I have some questions.

- Does serialization/de-serialization require dynamic memory allocation? From what I've seen it looks like it doesn't.

- How are you handling endianness? floating point numbers?

- How complete/tested is the library? Are you aware of projects using it?


I suggest looking at nanopb to compare, that’s how I’ve solved this problem in the past. It’s just a couple kB on an AVR.


I've used nanopb in the past. It's a great library. The only drawback is that it requires a type compilation step that generates the sources.


Thanks for considering using libnop!

This library works well in an embedded context. I've personally used it on Cortex-M class micro controller firmware.

The serializer/deserializer does not require dynamic memory allocation. Whether or not dynamic allocation happens depends on how you use the library. If you avoid using data types that perform dynamic allocation (e.g. std::vector) and use (or write) Reader/Writer types that use static memory (e.g. nop::BufferReader/nop::BufferWriter) then you should be fine.

There are some nice tricks you can use to permit protocols that have convenient dynamic containers on the host and static versions on the embedded device:

https://github.com/google/libnop/blob/master/docs/getting-st... https://github.com/google/libnop/blob/master/docs/getting-st... https://github.com/google/libnop/blob/master/examples/shared...

Endianness is assumed to be little because the vast majority of hardware is little endian. There are utilities to convert here:

https://github.com/google/libnop/blob/master/include/nop/uti...

These are not currently used by the Reader and Writer types included with libnop for efficiency, but are available for you to use in your own Reader and Writer types if you really need it.

Floating point is a much stickier problem due to lack of standardization across hardware. The library does not address this automatically and just packs floating point types in machine order. However, the library provides tools to help address the issue. One approach that works well is to use a fixed point representation for the wire type -- a value wrapper type is convenient for automating this:

https://github.com/google/libnop/blob/master/docs/getting-st...

See the Fixed template in the example. This is especially handy if you have a micro controller without floating point support or you want to minimize the size of the payload at the cost of range and/or precision.

The library has a complete suite of tests and 97.9% line coverage according to GCOV, with particular attention to conditional and error paths.

We use the library for a few internal embedded prototypes. I have not tracked its usage outside of Google since its recent release.

Best, C


> Endianness is assumed to be little because the vast majority of hardware is little endian ... libnop for efficiency

Do you have any evidence of this? I would be flabbergasted if a modern compiler couldn't optimize out a no-op endiannes conversion.


For the most part the optimization works well. The more subtle issue is that the union trick used by the endian utilities is not compatible with constexpr expressions. There are some interesting use cases for constexpr serialization that fail if the conversions are interposed in the Reader/Writer types. The alternative is to use reinterpret_cast, which is also incompatible with constexpr expressions.

Moreover, endian conversion templates are unusually frustrating in the current C++ standard. I wish there were a better way, but one does not currently exist.


Thanks for the detailed answer. Looks like it is a good fit for an embedded platform.


Supporting embedded platforms is one of the objectives of the library. Feel free to open a ticket on github if you have any further questions.


Why C++14 dependency? A lot of other projects are still stuck on C++11 because of variety of reasons. It's sad to see this dependency :(.


I tried to keep it C++11, but generalized lambdas are critical in important use cases (see nop::Variant). Besides, GCC and Clang have solid C++14 support. C++17 is another story... ;-)


The problem is not compiler support but the fact library is going to get used with other code base that is still C++11. Typically projects build system will enforce this and it’s hard to change without full evaluation of all dependencies. Is it possible to add support for C++11 and turn of these additional features that might not get used?


Serialization is part of the story. It would be great if someone can also write header-only dependency-free cross-platform RPC library :).


Since you mention it, check out the experimental RPC support:

https://github.com/google/libnop/blob/master/examples/interf... https://github.com/google/libnop/blob/master/include/nop/rpc...

I haven't documented it yet since it's not fully baked, but it's quite functional nonetheless. I have a working prototype of RPC over USB between a host PC and a Cortex-M micro controller. The ability to define constexpr dispatch tables is very convenient in a micro controller environment.


What transport do you use? http? Do you use any dependency for handling transport layer? I haven’t seen a good clean dependency less headed-only approach for this yet.


The library is transport agnostic. There is a Reader/Writer abstraction to adapt to any transport you like.

https://github.com/google/libnop/blob/master/docs/getting-st...


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: