I hear "C++" and yet what I see looks like 99% plain C, including the absurd habit of spamming "struct" in front of every function argument. No constexpr in sight, "inline" as if any compiler cared for the last 3 decades (outside of definitions in header files to resolve multiple definitions). Just rename the file to .c, replace "cin" and officially make it the C benchmark. At least that way one could add a comparison to see how much "actual" (read: "modern", read "not outdated for 15 years") C++ would hurt performance.
@CodingWithTom-tn7nlКүн бұрын
C is still valid c++ code, as c++ was supposed to be just C with classes. I avoid modern c++ like the plague and ignore 90% of its features because I can barely read a word of it compared to c. I basically write C++ like C but with std::cout (it's easier than remembering print codes), vectors and a bool type. The code is in the github and anyone is welcome to make a modern c++ version so we can see that difference.
@TheTrienco17 сағат бұрын
@CodingWithTom-tn7nl After spending some time in the code it's safe to say that you don't just avoid "modern" C++ like the plague. Saying "I avoid it, because I can barely read it" sounds a lot like "I avoid C++, because otherwise I'd have to learn it". There is nothing wrong with sticking to C-style, especially when keeping up with C++ is a full-time job, but... Comparing the C and cpp folder, it's basically "I copy/pasted the C code and did find/replace TRUE -> true, FALSE -> false". Even the outputs are almost all still printf. Especially in the context of comparing languages that just seems pointless and potentially misleading to anyone who thinks it's a good representation of those compared languages.
@kalasmournrex1470Күн бұрын
Go packages are awful. They can’t have circular dependencies because the go team are lazy/bad compiler writers.
@kalasmournrex1470Күн бұрын
Skill issues
@shm2362 күн бұрын
18:45 Stack is limited in Debug mode cuz Rust prefers to assume the stack is small so you are less likely to get surprised in prod. 20:45 You just cant have circular dependencies. Splitting it into separate crates or sequestering out your build.rs dependencies into their own file makes it cleaner.
@maniacZesci2 күн бұрын
Rust is language you don't use if you want things to work quickly if you don't know Rust, which you don't.
@CodingWithTom-tn7nlКүн бұрын
Of course someone with more experience than me in Rust would convert this a lot faster but either way less strict languages would be faster. I had almost zero experience coding in Go before this and converted it in one 1 hour. I know Zig pretty well but It doesn't matter how well I learn it, I still code in it slower than other languages because of the strict compiler. Being safer makes things slower.
@iampallob9404 күн бұрын
a good one…you just convinced me to subscribe with your content quality . thanks 🎉🎉
@raykirushiroyshi27525 күн бұрын
1:38 you probably misunderstood how packages work. You import a literal folder,when you change the folder name,it doesn't work. You should change the name of the package within the file and try again and see what happens. I know this for a fact since for one of my projects I import the package based on folder name, but the package declaration has a different name then the folder it's in. Iirc, package names are not important,it only matters that .odin files within the same directory have the same package declaration 3:05 you can look up functions on the odin website, you just navigate to packages and there you can type whatever in the search bar. Very useful since chatgpt nor googling will help you with odin :')
@CodingWithTom-tn7nl5 күн бұрын
One of the main reasons I started making this series is there isn't much information online. I try to do as much research as I can but only find the Odin website usually. So I can be mistaken. I normally try to just make things and follow what the compiler tells me.
@s-k4r5 күн бұрын
This is awesome!
@simonclemente6 күн бұрын
Man that's actually a good and simple explanation. keep it up!
@ferdynandkiepski50266 күн бұрын
8:46 the actual fast way to do it is with intrinsics that map to instructions specifically tzcnt on x86. Other important instructions for bitboards are pext and lzcnt and lead to large speedups.
@ohmygosh61768 күн бұрын
Omg i love the idea of int specify your own bit. I don't like how there is inconsistency in syntax when declaring an array/slice.
@EightSixx9 күн бұрын
zig has fmt so don't have to worry about where the bracket go :D
@synthbrain31739 күн бұрын
yeap Odin quite cool, easy to use and create some and also blazingly fast - 10 \ 10
@Nellak201110 күн бұрын
I have been toying around with the C# code and the original is horrible. I added a new linter that had sensible rules and performed several refactors using built-in VSCode code actions such as merge if statement. === No Refactors 2525 LOC === Layer 0 2300 LOC - Turned on the linter === Layer 1 1680 LOC - Removed Extra white space - Put braces inline rather than with white space - Put singlular statements on a single line instead of spaced if under 110 characters - Fixed OutOfBounds function to be a one liner - Removed unnecessary using === Layer 2 1480 LOC - Removed redundant if..else paths - Simplified conditionals - Denested conditionals - Removed unnecessary parentheses === Layer 3 (in progress) (39% the LOC of the original) < 1000 LOC - Began extracting functions and eliminating DRY violations ===== Summary The core logic would’ve been much clearer and simpler if the functions were modular and less tightly coupled. In the best case, we could get this down to under 500 LOC, but it’s so tangled that I won’t know for a few more days. ======= Performance Side-Note I converted the bitboard_array_global and castle_rights_global to readonly and saw a noticeable performance boost from 5500 ms to 4900 ms on my Laptop. Which is 1.12x faster.
@arngorf10 күн бұрын
16:00 Where I last worked , it was very important to use the size_t in our C code, and generally _t variants. I'm still learning low level programming, but I think it was to do with it being embedded software having to run on many different platforms. I also couldn't use bool because we had a few platforms (or compilers for those platforms I guess) that didn't support this type.
@aftalavera10 күн бұрын
Next time try to centralize the camera on top of the code! i means intelligently
@Nellak201110 күн бұрын
A few refactoring tricks that are imperative language agnostic will help you. 1. Early return. --- old if (cond) { logic } else { logic return } --- new if (!cond) return .. logic 2. And instead of nested if. --- old if (cond) { if (cond2) { logic } } --- new if (cond && cond2) { logic } 3. Extraction of pure code. --- old const main = () => { pure calculation side effect pure calculation side effect } --- new // you can unit test calc1 and 2 instead of performing an integration test only on main const calc1 = (...params) => { logic } const calc2 = (...params) => { logic } const main = () => { r1 = calc1(...) side effect(r1) r2 = calc2(...) side effect(r2) } 4. Converting functions that call outside of themselves to ones that are self contained. (Only do this if possible and if it makes sense to, in rare cases in mutable languages you actually must write it in the poor style of external mutation, if you must do that, isolate it as much as you can to mitigate uncontrolled side-effects) --- old let i = 0 const f = i => { i++ } f(i) // i is mutated from f, making f non-deterministic and hard to debug --- new const f = i => i + 1 f(i) // i + 1 is returned but i itself is never mutated out from under you 5. Eliminating global variables and instead using block scoped variables. 6. Reducing mutable variables through higher order functions if applicable, such as using map, filter, and reduce. This simply reduces bug surface area, however, not all imperative languages have performant support for this. ==== General refactoring approach 1. Apply above transformations and more with the goal of concise code that is modular and easy to reason about. Clean code is easier to manipulate that ugly and overly mutable code. 2. Create Integration tests for functionality and unit tests for any pure functions you were able to extract. I recommend using property based testing approaches so you don't have to enumerate a billion example tests. 3. With the tests in place, you start slowly refactoring out with the goal of breaking up everything into self-contained functions. Functions should have 1 responsibility. Meaning that a function should do as few things as possible. The reason for this is so that you can make your system Composable rather than fragile. 4. With your composable building blocks tested, you can then build up more easily. ==== Overall, you should see a massive reduction in the lines of code and your code will be far better if you follow my advice. === Update I have seen very often people using switch statements which is super annoying and bloated. It is equivalent but more terse to use an object instead (atleast in JS, make sure your language you do this in supports hash maps). --- old switch (thing) { case ..: .. case .. : .. .... } --- new (JS syntax) const cases = { 'case1' : () => logic, 'case2' : value or whatever, ... } cases[thing] Alternatively: { 'case1' : () => logic, 'case2' : value or whatever, ... }[thing] // Note: usually you want the cases in your object literal to have a value and not be a function so that you can assign the result to a variable then do further logic // like so: const value = { 'case1' : value, 'case2' : value, ... }[thing] logic on that value ...
@smd58tx10 күн бұрын
Perfect timing for this video! I was just getting started with Odin and was running up against this kind of issue. Great explanation about how variables end up on the stack vs heap, and how to handle some basic cases.
@eduardabramovich121610 күн бұрын
The BIG BIB elephant in the room!
@Blonar25711 күн бұрын
Thank you very much. Very nice course!
11 күн бұрын
And there it is C++ gorgeous language. Still king!
@Enderofisziel11 күн бұрын
i love how rust segment is 3x times larger than others
@requestfx558512 күн бұрын
Why not use zig build system, "zig init" command helps
@CodingWithTom-tn7nl12 күн бұрын
It's probably better for a large project to use the build system, especially if you have multiple platform targets. For demonstrating things and just getting the code to run quickly, it's more cumbersome. To find the exe, you need to look through folders and normally I just want it to run in the console window. It's mostly preference and I don't feel like I need it. There is barely any different to typing: zig run main.zig vs zig build run
@requestfx558511 күн бұрын
@CodingWithTom-tn7nl valid. I still prefer the build system, zig init and zig build run and it already works. The second you need some module, dependency, c code stuff the build system becomes very useful
@dawid01155 күн бұрын
also zig build system provides caching where zig run does not
@eduardabramovich121612 күн бұрын
keep up the great work!
@gronki113 күн бұрын
Still no Fortran? You missed the most important language for high performance computations
@CodingWithTom-tn7nl13 күн бұрын
Maybe I can try Fortran at some point. I just have no experience in the language and got burned out trying out too many new languages.
@alsen9913 күн бұрын
You should try using C
@dolorsitametblue14 күн бұрын
13:20 About Nim literals - you can actually use 'u64' only on first value of array and compiler will infer that type for other literals.
@cyrilemeka698714 күн бұрын
Lovely odin lang videos. The sound quality of video is too low though, maybe a microphone issue?
@CodingWithTom-tn7nl14 күн бұрын
I have a new microphone, so shouldn't have sound problems now.
@cyrilemeka698713 күн бұрын
@CodingWithTom-tn7nl Oh, okay. Really enjoy your videos, keep up the good work
@realGeobor14 күн бұрын
Which of these languages would you consider enjoyable/unenjoyable? I write C++, and I don't hate it like some people do, but I do want to try some other languages just for fun like Odin, Zig, and Rust. Im heavily leaning towards Odin, and heavily leaning against Rust at this point.
@TheMachina4214 күн бұрын
Odin is the simplest, Zig is imo the most enjoyable one (but there is a 2 week period needed to stop thinking like C/C++, and adapt). Rust is the more C++ like imo but I don't really enjoy it, It's cool for string processing tho, Rust is almost like C++ but with sane defaults if that make sense.
@nupaulmiller641214 күн бұрын
For Rust what profile release optimizations did you use? Because i found an increase in performance by, of course using Release with Opt Level 3, but also using "thin" for the LTO (removed typo)
I would highly highly recommend you take a look at one of theprimeagen's recent videos with Casey Muratori where they talk about such comparisons between languages
@TheMachina4214 күн бұрын
To be fair this is a much better benchmark, as the algorithm isn't just a for loop within a for loop.
@dolorsitametblue14 күн бұрын
@@TheMachina42 they're still useless unless you go in detail and explain why one version is slower/faster. Until then you're comparing implementation bugs/quirks that experienced (in that language) developer would not write.
@TheMachina4213 күн бұрын
@@dolorsitametblue That's true, I think it's a reasonable benchmark about how simple it is to write code that runs to X level of performance, obviously not perfect, but it gives a good appreciation of what kind of performance you can expect.
@CodingWithTom-tn7nl14 күн бұрын
I'll update the speeds here if people make improvements: C: 339ms C#: 683.2ms C++: 331.2ms D: 383.8ms (compiled with ldc2 instead) was 787.4ms (with dmd) Go: 627.4ms Java: 1988ms (used longs and alternate algorithm for rook and bishop moves) Nim: 429ms (with flags: nim c -d danger -d:lto --passC:"-march=native") was 645ms in release Odin: 398ms - added no bounds check to perft function Python: 124536ms or 124 seconds just using ints and no numpy Python with pypy: 51139ms Rust: 463.03ms (made everything global) Swift: 585ms (with unsafeTemporaryAllocation) was 37,430ms Zig: 335.8ms Code: github.com/hcsalmon1/Chess-Engine-Test
@thedeemon11 күн бұрын
I managed to make Swift version faster than D, just 1.27x slower than C. See my PR in the repo. Many solutions in other languages - C, C#, D etc. - allocate moveList and pinArray on the stack which is essentially free. Meanwhile the original Swift version for moveList allocated an array of 250 heap-allocated arrays, and then spent enormous amount of time deallocating them all each time the recursive function exits. This means hundreds of allocations and deallocations in each recursive call where other languages got their arrays for free. Hence the difference.
@CodingWithTom-tn7nl11 күн бұрын
@@thedeemon I don't know why Swift made specifying stack variables so complicated. I didn't know that they would be heap variables. If I had known I would have probably just made a large global array for the moves. Thanks for the code update. I thought it was a windows issue and not heap array problem. I would have experimented a lot more with Swift it didn't take 2-3 minutes to compile each time. Edit: for some reason it was just behind Rust on my machine, maybe that is a Windows problem?
@thedeemon11 күн бұрын
@CodingWithTom-tn7nl You're probably using Swift 5.x while I use 6.0, and they may use different versions of LLVM. I noticed version 6 to be faster usually. On my machine with Linux I get: C - 632ms (gcc 14.2.1) Swift - 792 ms Rust - 1248 ms (using rustc -C opt-level=3 -C debuginfo=0 -C panic=unwind main.rs) (rustc 1.83.0) If I move those huge constants definitions to a separate file, it cuts Swift compilation time in half, but it's still ridiculous, yeah. I suppose its type inference engine is not suited for long array literals.
@thedeemon11 күн бұрын
I just made a PR for D version that makes it a bit faster than C btw.
@CodingWithTom-tn7nl11 күн бұрын
@@thedeemon The problem with reducing moves is that it makes the algorithm crash in other positions. The most moves in a chess position is about 220 but that's a ridiculous position with loads of queens. I made it 250 just to be safe. I've tested the opening position at depth 6 and the most moves is 46. If you reduce the moves in D to 50, I will need to do the same for all other languages and compare it too. Another strategy is to have a global array to store the moves: move_list[500][4]; move_counts[10]; or separate: starting_squares[500]; target_squares[500]; tags[500]; pieces[500]; move_counts[10]; Then you do this: move_counts[ply + 1] = GetMoves(ply); int move_count = move_counts[ply + 1] - move_counts[ply]; if (depth <= 1) { return move_count; } int nodes = 0; for (int i = move_counts[ply]; i < move_counts[ply + 1]; i++) { //make move nodes += Recursion(depth - 1, ply + 1); //unmake move } 500 won't be enough for some positions but the starting position will be fine. The problem is testing all of these variations in all other languages. -Reducing the moves to 50 -Using global move array of 500
@LTibor201214 күн бұрын
I have tested your D lang algorithm on my 14 years old Asus X52F laptop with 3 different compilers. Operating system: Linux Mint 22.1 beta, processor i3 M370, 8 GB DDR3 RAM Compilers: gdc 13.3, dmd 2.109.1, ldc2 1.36 (LLVM 17.0.6) Compile, exe file size, and run times in milliseconds 1. ldc2 -O3 -release -of=main main.d 2.4 MB 1843 ms 2. gdc -O3 -frelease -o main main.d 2.4 MB 1868 ms 3. dmd -O -inline -release -of=main main.d 3.9 MB 2696 ms
@skilletpan567414 күн бұрын
No Free Pascal, Fortran or bash?
@meryplays895214 күн бұрын
Double the first two.
@danny-jo1rb15 күн бұрын
was C++ faster than C because in C++ bools are 1 byte and u used 4 byte ints in the C code?
@vytah15 күн бұрын
I already said you do not need BigIntegers for Java, you can just use longs. I sent you a PR.
@desertfish7415 күн бұрын
Maybe you can try Kotlin on the JVM, it does have ulong types. And if you paste Java code into IntelliJ it can try to convert it to kotlin automatically. I haven’t had great results with that latter feature on larger code size though, it then produces horrible style but that can be cleaned up afterwards
@MrWorshipMe15 күн бұрын
In c++ you didn't use bits for the least significant one digit's position. It's probably more optimized than your code.
@jonklaric15 күн бұрын
If you were up for it, I’d be curious to see a C vs C++ vs comparison too. Great video though, thanks!
@Lena-yt3yl15 күн бұрын
Regarding C# array[x][y] exists and does roughly what you expect it to, but there is a difference how access is processed between var[x, y] and var[x][y]
@CodingWithTom-tn7nl15 күн бұрын
I made an updated version to this video. Here's the Github with the updated code: github.com/hcsalmon1/Chess-Engine-Test
@Daniel-fl4si15 күн бұрын
You hate too much things, don’t you?
@nonae941915 күн бұрын
I don't have a idea why, but C++ was faster than C. 1) Maybe it's because the compiler developer didn't write "if original_languge == C++ then emit sleep (1000) everywhere". I would like to know why you expect something else? 2) Measure variance. The difference for C, C++, and Zig should have a insignificant p value.
@asdfasdf-is3xf15 күн бұрын
For nim, because arrays only have a single type, you only need to mark the type of the first value in it and it will propegate that type to the rest of the values if possible. I'm sorry that nobody mentioned that. Also I hope you compiled nim in release mode and not the default debug mode. I don't remember how significant the difference is though.
@CodingWithTom-tn7nl15 күн бұрын
I wish I had known that, thanks. I always tried to use release mode in all test.
@meryplays895214 күн бұрын
@CodingWithTom-tn7nl Can you please-please-please update the timings for Nim based on the above observations?
@CodingWithTom-tn7nl14 күн бұрын
@ It was already in release mode. I just tested regular debug mode vs release now: Nim debug: 13 seconds Nim release: 0.722 seconds I had things running on my computer so it was a bit slower that in the video. There is a big difference between debug and release. Removing u64 from the constants wouldn't affect speed at all. It's the same code, just less annoying to write.
@asdfasdf-is3xf14 күн бұрын
@CodingWithTom-tn7nl release mode is the recommended way to compile nim code, but there's another mode called danger that removes things like extra bounds checks. You can also use (iirc) "-d:lto" to enable link time optimization, and --passC:"-march=native" to let the compiler make system optimized assembly. I'm not sure whether other languages do any of these things by default
@CodingWithTom-tn7nl14 күн бұрын
@@asdfasdf-is3xf flags: nim c -d:danger -d:lto --passC:"-march=native" speed: 533ms I've updated the pinned comment with the new speed.
@Zihad15 күн бұрын
Is the code available on like a GitHub repo? I want to take a look at the Rust one specifically.
@destiny_0215 күн бұрын
would be nice if you show us the compiler flags as well
@CodingWithTom-tn7nl15 күн бұрын
CPP: Release mode 64 bit, in visual studio C: GCC -O3 C#: Release mode 64 bit, in visual studio D: --release Go: no flags nim: nim c -d:release main.nim Odin: -opt:2 -no-bounds-check Python: no flags Rust: cargo build --release in cargo.toml: [profile.release] opt-level = 3 Swift: -release (I think) Zig: -O -releaseFast
@destiny_0215 күн бұрын
@CodingWithTom-tn7nl using different compilers for C and C++, that explains the performance difference. try using -O3 -march=native -static -flto -s for even more performance. also would be interesting to see CLANG vs GCC vs MSVC(visual studio). often clang produces faster executables.
@thedeemon14 күн бұрын
@CodingWithTom-tn7nl For D it should be "ldc2 -O --release main.d" For Swift it should be "swiftc -O main.swift"
@Evansgr12315 күн бұрын
I believe the zig compiler doubles as a c compiler too - have you considered compiling your c code using the zig compiler to see if you get different results for the c program? Not sure if zig can also compile c++ though.
@destiny_0215 күн бұрын
yes it can, but the c and c++ compiling functionality is actually handled by clang compiler. main benefit to using zig c++ over clang is it has better cross compilation support and can produces smaller binaries (since all headers are compiled from source with your custom flags, rather than just linking precompiled library)
@CodingWithTom-tn7nl14 күн бұрын
I'll try it and see the difference. Edit: zig cc -O3 main.c Constants.c 351ms It was the same speed.
@sharp76415 күн бұрын
Why is Python even there lol is like comparing race cars with a drawing of a car.. I guess if you use Python with pypy it would make sense but those are compiled language vs an interpreted one
@thedeemon15 күн бұрын
Regarding D: first, you're using the wrong compiler. To generate fast code use LDC, it's LLVM based and should be close to C++ in performance. Especially in your task where there are basically no allocations, so GC is not a problem at all. Second, vscode supports D pretty fine, just install the extension named "D Programming Language (code-d)".
@CodingWithTom-tn7nl15 күн бұрын
I downloaded ldc2 and ran the test again: .\ldc2.exe main.d -release 438 ms