| ▲ | pornel 4 days ago |
| Note that Fil-C is a garbage-collected language that is significantly slower than C. It's not a target for writing new code (you'd be better off with C# or golang), but something like sandboxing with WASM, except that Fil-C crashes more precisely. |
|
| ▲ | thesz 4 days ago | parent | next [-] |
| From the topic starter: "I've posted a graph showing nearly 9000 microbenchmarks of Fil-C vs. clang on cryptographic software (each run pinned to 1 core on the same Zen 4). Typically code compiled with Fil-C takes between 1x and 4x as many cycles as the same code compiled with clang" Thus, Fil-C compiled code is 1 to 4 times as slow as plain C. This is not in the "significantly slower" ballpark, like where most interpreters are. The ROOT C/C++ interpreter is 20+ times slower than binary code, for example. |
| |
| ▲ | silotis 4 days ago | parent | next [-] | | Cryptographic software is probably close to a best case scenario since there is very little memory management involved and runtime is dominated by computation in tight loops. As long as Fil-C is able to avoid doing anything expensive in the inner loops you get good performance. | | |
| ▲ | thesz 4 days ago | parent [-] | | > best case scenario since there is very little memory management involved and runtime is dominated by computation in tight loops.
This describes most C programs and many, if not most, C++ programs. Basically, this is how C/C++ code is being written, by avoiding memory management, especially in tight loops. | | |
| ▲ | silotis 4 days ago | parent [-] | | This depends heavily on what problem domain you're talking about. For example, a DBMS is necessarily going to shuffle a lot of data into and out of memory. | | |
| ▲ | quotemstr 4 days ago | parent | next [-] | | It depends. Consider DuckDB or another heavily vectorized columnar DB: there's a big part of the system (SQL parser, storage chunk manager, etc.) that's not especially performance-sensitive and a set of tiny, fast kernels that do things like predicate-push-down-based full table scans, ART lookups, and hash table creation for merge joins. DuckDB is a huge pile of C++. I don't see a RIIR taking off before AGI. But you know what might work? Take current DuckDB, compile it with Fil-C, and use a new escape hatch to call out to the tiny unsafe kernels that do vectorized high-speed columnar data operations on fixed memory areas that the buffers safe code set up on behalf of the unsafe kernels. That's how it'd probably work if DuckDB were implemented in Rust today, and it's how it could be made to work with Fil-C without a major rewrite. Granted, this model would require Fil-C's author to become somewhat less dogmatic about having no escape hatches at all whatsoever, but I suspect he'll un-harden his heart as his work gains adoption and legitimate use-cases for an FFI/escape hatch appear. | | |
| ▲ | kragen 4 days ago | parent [-] | | > DuckDB is a huge pile of C++. I don't see a RIIR taking off before AGI. While I'm not a big fan of rewriting things, all of DuckDB has been written in the last 10 years. Surely a rewrite with the benefit of hindsight could reach equivalent functionality in less than 10 years? | | |
| ▲ | jitl 4 days ago | parent [-] | | the sqlite RIIR is going quite well: https://turso.tech/blog/beyond-the-single-writer-limitation-... (sqlite is quite a bit smaller than DuckDB tho) | | |
| ▲ | zX41ZdbW 3 days ago | parent | next [-] | | I'm trying to make Turso load some data, but it is so slow that even several months are not enough to load the dataset: https://github.com/ClickHouse/ClickBench/issues/336 | |
| ▲ | kragen 4 days ago | parent | prev [-] | | Is it? It's much less new. | | |
| ▲ | jitl 4 days ago | parent [-] | | for one, duckdb includes all of sqlite (and many other dependencies). it knows how to do things like efficiently query over parquet files in s3. it's expansive - a swiss army knife for working with data wherever it's at. sqlite is a "self contained system" depending on no external software except c standard library for target os: > A minimal build of SQLite requires just these routines from the standard C library: > memcmp(), memcpy(), memmove(), memset(), strcmp(), strlen(), strncmp() > Most builds also use the system memory allocation routines: > malloc(), realloc(), free() > Default builds of SQLite contain appropriate VFS objects for talking to the underlying operating system, and those VFS objects will contain operating system calls such as open(), read(), write(), fsync(), and so forth Quoting from the appropriately named https://sqlite.org/selfcontained.html as a very rough and unfair estimate between the two project's source, sqlite is about 8% the size of duckdb: $ pwd
/Users/jitl/src/duckdb/src
$ sloc .
---------- Result ------------
Physical : 418092
Source : 317274
Comment : 50113
Single-line comment : 46187
Block comment : 3926
Mixed : 4415
Empty block comment : 588
Empty : 55708
To Do : 136
Number of files read : 2611
----------------------------
$ cd ~/Downloads/sqlite-amalgamation-3500400/
$ sloc .
---------- Result ------------
Physical : 34742
Source : 25801
Comment : 8110
Single-line comment : 1
Block comment : 8109
Mixed : 1257
Empty block comment : 1
Empty : 2089
To Do : 5
Number of files read : 2
----------------------------
| | |
|
|
|
| |
| ▲ | thesz 3 days ago | parent | prev | next [-] | | I am professional database developer. We do not do what you are thinking we are doing. ;) | | |
| ▲ | silotis 3 days ago | parent [-] | | I was thinking less about the DB data itself and more about temporary allocations that have to be made per-request. The same is true for most server software. Even if arenas are used to reduce the number of allocations you're still doing a lot more memory management than a typical cryptographic benchmark. |
| |
| ▲ | jandrewrogers 4 days ago | parent | prev [-] | | Most databases do almost no memory management at runtime, at least not in any conventional sense. They mostly just DMA disk into and out of a fixed set of buffers. Objects don't have a conventional lifetime. | | |
|
|
| |
| ▲ | vacuity 4 days ago | parent | prev | next [-] | | Along with the sibling comment, microbenchmarks should not be used as authoritative data when the use case is full applications. For that matter, highly optimized Java or Go may be "1 to 4 times as slow as plain C". Fil-C has its merits, but they should be described carefully, just with any technology. | | |
| ▲ | thesz 4 days ago | parent | next [-] | | I replied to unwarranted (to my eye) call that Fil-C is significantly slower than plain C. Fil-C has its drawbacks, but they should be described carefully, just with any technology. | | |
| ▲ | vacuity 4 days ago | parent [-] | | I maintain that microbenchmarks are not convincing, but you have a fair point that GP's statement is unfounded, and now I've made a reply to GP to that effect. |
| |
| ▲ | nicoburns 4 days ago | parent | prev [-] | | Or JavaScript for that matter |
| |
| ▲ | MangoToupe 4 days ago | parent | prev [-] | | What does "significantly" mean to you? To my ear, "significantly" means "statistically significant". |
|
|
| ▲ | quotemstr 4 days ago | parent | prev | next [-] |
| WASM is a sandbox. It doesn't obviate memory safety measures elsewhere. A program with a buffer overflow running in WASM can still be exploited to do anything that program can do within in WASM sandbox, e.g. disclose information it shouldn't. WASM ensures such a program can't escape its container, but memory safety bugs within a container can still be plenty harmful. |
| |
| ▲ | CryZe 3 days ago | parent | next [-] | | You can buffer overflow in fil-c and it won't detect it unless the entire buffer was its own stack or heap allocation with nothing following it (and also it needs to be a multiple of 16 bytes, cause that's padding that fil-c allows you to overflow into). So it arguably isn't much different from wasm. Quick example: typedef struct Foo { int buf[2];
float some_float;
} Foo;int main(void) { Foo foo = {0};
for (size_t i = 0; i < 3; ++i) {
foo.buf[i] = 0x3f000000;
printf("foo.buf[%zu]: %d\n", i, foo.buf[i]);
}
printf("foo.some_float: %f\n", foo.some_float);
}This overflows into the float, not causing any panics, printing 0.5 for the float. | |
| ▲ | bonzini 3 days ago | parent | prev [-] | | At least WASM can be added incrementally. Fil-C is all or nothing and it cannot be used without rebuilding everything. In that respect a sandbox ranks lower in comprehensiveness but higher in practicality and that's the main issue with Fil-C. It's extremely impressive but it's not a practical solution for C's memory safety issues. |
|
|
| ▲ | galangalalgol 4 days ago | parent | prev | next [-] |
| What language do people considering c as an option for a new project consider? Rust is the obvious one we aren't going to discuss because then we won't be able to talk about anything else, Zig is probably almost as well loved and defended, but it isn't actually memory safe, just much easier to be memory safe. As you say, c# and go, also maybe f# and ocaml if we are just writing simple c style stuff none of those would look all that different. Go jhs some ub related to concurrency that people run into, but most of these simple utilities are either single threaded or fine grained parallel which is pretty easy to get right. Julia too maybe? |
| |
| ▲ | summarity 4 days ago | parent [-] | | In terms of GC quality, Nim comes to mind. | | |
| ▲ | galangalalgol 4 days ago | parent [-] | | I keep ignoring nim for some reason. How fast is it with all the checks on? The benchmarks for it julia, and swift typically turn off safety checks, which is not how I would run them. | | |
| ▲ | cb321 3 days ago | parent | next [-] | | Since anything/0 = infinity, these kinds of things always depend upon what programs do and as a sibling comment correctly observes how much they interfere with SIMD autovectorization and sevral other things. That said, as a rough guideline, nim c -d=release can certainly be almost the same speed as -d=danger and is often within a few (single digits) percent. E.g.: .../bu(main)$ nim c -d=useMalloc --panics=on --cc=clang -d=release -o=/t/rel unfold.nim
Hint: mm: orc; opt: speed; options: -d:release
61608 lines; 0.976s; 140.723MiB peakmem; proj: .../bu/unfold.nim; out: /t/rel [SuccessX]
.../bu(main)$ nim c -d=useMalloc --panics=on --cc=clang -d=danger -o=/t/dan unfold.nim
Hint: mm: orc; opt: speed; options: -d:danger
61608 lines; 2.705s; 141.629MiB peakmem; proj: .../bu/unfold.nim; out: /t/dan [SuccessX]
.../bu(main)$ seq 1 100000 > /t/dat
.../bu(main)$ /t
/t$ re=(chrt 99 taskset -c 2 env -i HOME=$HOME PATH=$PATH)
/t$ $re tim "./dan -n50 <dat>/n" "./rel -n50 <dat>/n"
225.5 +- 1.2 μs (AlreadySubtracted)Overhead
4177 +- 15 μs ./dan -n50 <dat>/n
4302 +- 17 μs ./rel -n50 <dat>/n
/t$ a (4302 +- 17)/(4177 +- 15)
1.0299 +- 0.0055
/t$ a 299./55
5.43636... # kurtosis=>5.4 sigmas is not so significant
Of course, as per my first sentence, the best benchmarks are your own applications run against your own data and its idiosyncratic distributions.EDIT: btw, /t -> /tmp which is a /dev/shm bind mount while /n -> /dev/null. | |
| ▲ | adgjlsfhk1 4 days ago | parent | prev [-] | | In Julia, at least, bounds checks tend to be a pretty minor hit (~20%) unless the bounds check gets in the way of vectorization |
|
|
|
|
| ▲ | vacuity 4 days ago | parent | prev | next [-] |
| A GC lang isn't necessarily significantly slower than C. You should qualify your statements. Moreover, this is a variant of C, which means that the programs are likely less liberal with heap allocations. It remains to be seen how much of a slowdown Fil-C imposes under normal operating conditions. Moreover, although it is indeed primarily suited for existing programs, its use in new programs isn't necessarily worse than, e.g., C# or Go. If performance is the deciding factor, probably use Rust, Zig, Nim, D, etc. . |
|
| ▲ | fithisux 3 days ago | parent | prev [-] |
| Test with Fil-C, compile with gcc into production. Easy. |