@@random_userk123 the person leaving the comment is being sarcastic, and they *do* want people to see the comment. people will see it and like the comment so more people see it, thus "defeating" the person who left the comment.
@@TheMCEnthusiastPlays WAIT! you are saying that this person is an attention grabber?! *gasps in shock and horror* Who could ever do such a evil thing?! He MUST be punished for it!
Ok, but also find guy with slow old laptop, and make sure that game is also fast enough on his laptop with limited resources, and if is not, then you should try to optimize
It depends on the game. But if you're making a simple 2d action platformer, or a card battler/TCG, or a top down roguelike etc. then your game should absolutely run fine on like a 2008 computer, and if it isn't maybe you should optimize it
I prematurely optimize not to achieve better performance, but because that's my default coding style. Example: - constexpr when possible; - allocate objects on stack when possible; - prefer std:array over std::vector and std::vector over other containers, unless container is large and elements needs to be removed from begin/middle; - reuse heap allocated objects when possible; e.g. network read/write buffer; - one line member functions define in it's class definition, so it would be inlined; - large objects or objects who allocates memory on heap internally, pass around as const &/*; - RVO/NRVO; - if needed to loop over large amount of data, maybe use struct of arrays (SOA) instead of array of structs (AOS); - avoid interfaces when possible; Of course i would not sacrifice code readability for questionable performance gains.
I would definitely agree that performance is not the cause for concern with that crafting system. In my eyes, it's how time consuming and error prone manually entering all the different variants of a single recipe will get.
I find writing the project out on paper and doing proper planning before starting saves a lot of headaches. Writing tests and benchmarks also helps before committing code, it's a bit more hassle but worth it.
The first example you show isn't an optimization that "ended up being slower"... It's a bug that surprisingly didn't crash the program lol. It's slower because it's checking past the end of the array into what is now other data, but still treating it as the previous type....
@@Dimblar the profile is entirely pointless if it's not measuring against the same control... Obviously a loop will take longer if it's checking more items; which is exactly what's happening. The ONLY way that the profile means anything at all, is if the early return condition is at the same index within the array for both checks... Which is CLEARLY not the case, as the profile took longer when it is obviously doing less work. Take a dynamically allocated, null-terminated string (so the compiler can't optimize the loop exit condition/unroll), and use the null terminator as the only exit condition - and it WILL end up being faster. What's the point of a misleading example?
@@Hazanko83 It is not a misleading example, the point is to profile or measure the code with a tool not eyeball it. This control and solution is not going to be known to beginners, so the solution is to do as I have just said.
@@Dimblar It's like trying to record the speed of a cheetah, but you throw it off a cliff. It's not measuring what you think it is. I'm more than familiar with profiling, and wrote a custom profile that I can litter through my game engine where I need it (it's not hard). If beginners look at this code, and think it's an actual optimization, they're going to be really confused why they can't get a stable program running. Optimizations will always be faster, or they wouldn't be optimizations... that's why it's hard to show an example where it fails. If he's trying to teach beginners in the method of "don't care about the details just listen", then he shouldn't show code that will 100% crash your program, and he shouldn't show profiles that are measuring two completely different things and showing them side by side. Either he's aware he's showing a contrived and dangerous example and just needed filter for the video(as an opening nonetheless), or he's unaware and any good teacher wouldn't want to be teaching something incorrect.
For me it’s you should optimise things that are called very very often like multiple times per second As for the other part of code being called way less often just make sure it works forget optimise. once the project it’s finished you can start doing more heavier optimisation
A few years back I got back into some game development for the first time in 30 years. I was amazed at how inefficient for example Unity and Godot actually are as frame works, but PCs became perhaps 4000x faster in the 34 years since i programmed my last game on a Atari ST 68000 8MHz parts I had even written in assembly to keep the speed. It almost feels freeing to make a side scroller shmup and just do everything in the game loop without trickery :D Especially when I used Zig and Raylib, no added game engine overhead. 1200+ fps on a Mac Book m1 :D It's insanely fast without "optimisation" Although I have an innate urge to optimise especially for memory usage, that comes as second nature to me. Having programmed my first game on a 3KB VIC20 :D But it's trivial to have a well performing 2D game these days.
What bothers me is that Unity is a big company that can afford to perform optimizations but choose not to. More features = better to them at the cost of building on top of deprecated everything. I am stuck in the Unity landscape at work because nobody wants to get their hands dirty and make their own engine.
I'm glad you included the first type and the third type for contrast. Some early structural choices can limit later options, important operations in a game may be very latency sensitive but low throughput, versus some program that does scientific number crunching which is not affected by latency but requires high throughput per joule. Even selecting the language can be considered an initial optimization yet could not be considered premature. Language selection could also be in the pessimization category, if you start with a slow highly abstracted language then writing the prototype code may be much faster and have fewer obvious bugs, but eventually you may have complications when optimizing.
You should also talk about FPS. People very often ignore improvements like 10 from 11 FPS (90ms difference), but panic when uncapped game goes from 600 to 500 (0.3 ms difference). And other common problems like running all logic every frame along rendering, not using tools like Nvidia Insight to see what is actually going haywire under the hood via frame debugger, and not resetting chrono properly (start value taken right before while loop, end value including cost of outputting all debug info), etc
Agreed. Professionals measure frame time in milliseconds (or even nanoseconds.) Amateurs measure FPS which becomes *more and more inaccurate the higher the frame rate.*
Yes, but I decided to simply pull out a pointer variable declaration from a loop into the parent scope and suddenly it’s 12% faster, even with -O3 and vectorization enabled. This loop had a nested loop, and I was executing 7,998,000 2D AABB collision checks every run, and I had such things like ‘__restrict’ on my pointers. I thought it was a fluke so I retested several times, and then I reordered the tests to run the slow one first and the fast one second, yet the result was the same. I was using Clang 16.0.5 at the time.
"Premature Optimization" does not exist! There is only: Proactive Optimization - designing things effectively before you start building Reactive Optimization - refactoring tested parts to make them perform as expected Unnecessary Optimization - optimizing things that don't need to be optimized, or things that you don't need at all Aim for proactive, be prepared for reactive, avoid unnecessary.
I felt that in my soul. I once relied on ++i to check that i hit the end of an array and then give a "value not found" error. It's the little weird stuff that'll mess with overall function @hoardingapples7083
Yep very true, people like to argue on which piece of code is faster but almost never measure it's performance, and also like to say stuff "oh if this function gets called 1 million times it's gonna be slow". I think it's called "Bike Shedding". The reality is that sometimes people forget that they need to optimize code after they measure that's it's actually slow. I'm unhinged fanatic when it comes to code optimization, but I still don't waste my time on optimizing code that doesn't need it. I use this mentality when I'm coding: 1. ooga booga working code first 2. if code is too slow, I measure which part of it is slow (this is very important) and optimize it (and fix bugs, ugly parts etc) 3. repeat first step Also sometimes it takes few iterations of code to make it fast and working, like face culling code for example, it was easy for me to make it fast, but it was producing wrong results Very good video btw👍
The simpler and clearer you write code for people, the easier it is for the compiler to optimize this code (but not always). Before any optimizations of processor or memory usage, do a benchmark, evaluate the contribution of costs against the overall background, and only then proceed with optimizations.
Im not a developer by any means but having to deal with grass caches in skyrim mods and constantly updating it each time a plugin touches it just to add "free" fps makes your point sooo true and valid
i was making big integer that has no limits in size, like in python. And in this case reserving memory before doing push_backs was really important. But this project was focused in optimization from the start because big integer has no limits. But some fast algorithms like Karatsuba's multiply was slower than simple algorithm so keep it simple
This is correct. Unfortunately, beginners are not able to distinguish between optimization and non-pessimisation. So I would recommend that they don't attempt either of them. If you are still learning C/C++, just write code that works and that you can understand.
I actually just spent the last two days, un-optimizing a part of my game engine, for the sole purpose of encouraging future localization. The hit to performance is pretty minimal, and it actually makes all of my game's data files, slightly smaller, as a nice bonus.
This is why planning is key. If the plan calls for only 3 items, there’s no point making some generic optimised inventory system that can support a million items and run in O(1) time
1. Program for correctness. 2. Profile. 3. If you need more performance, focus on what is taking the most CPU. 4. Do not sacrifice correctness. 5. Keep the correct version around for a programmer to read to see what is meant to be happening. 6. If reading the optimised code is not clear, then use comments to explain both what it's meant to do, what optimisations you've applied, and so on so that a programmer reading it can easily work out what's going on rather than having to try to decode the kind of cryptic mess that optimisation usually results in.
For example doom was revolutionary in 3d fps, when it came, pc didn't have even 1/10 of todays pc power, and they didn't even do much optimization, but this game is still game in the hall of fame
Когда я начинал делать игру на FNA, я думал, что заниматься оптимизацией мне придётся ещё не скоро, ведь игра то простенькая. И очень удивился, когда увидел 15 fps. Это 2д top-down игра с процедурной генерацией. Мир в ней генерируется полностью за раз. Так вот, я сделал генерацию и всё было в порядке, однако, стоило мне немного увеличить мир, как fps просаживался. Я понял, что в игре отрисовываются даже те тайлы, которые не видны на экране, поэтому решил сделать так, чтобы отрисовывались только те, что должны быть видны. Я это сделал, это сработало. Однако... Когда я был не дома, хотел поработать над игрой на своём ноутбуке, который гораздо слабее моего основного ПК. Тогда я заметил, что, fps хоть и повышается, когда я отрисовываю только нужные тайлы, всё равно, чем больше мир, тем менее плавно работает игра. Какое-то время я не мог понять, в чём дело. Потом до меня дошло озарение, что виноват цикл, перебирающий абсолютно каждый тайл, чтобы проверить, близко ли он к камере. Я начал думать, что можно сделать, и решил разделить мир на чанки. (Что забавно, я не стал менять способ генерации, а просто начал разбивать на чанки уже сгенерированный мир) Сначала я планировал уменьшить количество переборов циклом. Мир 1000 на 1000 с чанками размером 10 на 10, перебирался бы куда меньшее количество раз, если перебирать чанки. Но потом я понял, что от лишних переборов можно вообще избавиться, если по позиции камеры понять, в каком она чанке. Так я начал отрисовывать только чанк, в котором камера, и близлежащие к нему чанки. Это позволило поднять fps в мире 10.000 на 10.000 с 2, до 2000. Ха-ха, конечно, я думаю видео немного о другом, потому что мои проблемы были вызваны большой неопытностью, но всё-таки, мне хотелось поделиться своей историей с оптимизацией 😃 Upd: после этой истории я понял, почему в Майнкрафте используют систему тиков, лол
hm idk what to say about this. If you have let's say 1000 elements to push back and your vector isn't pre sized, it will visibly lag your game. It is probably the first optimization you can do
@@lowlevelgamedev9330 For new vectors yes, if you're re-using vectors however their allocators significantly overprovision memory to ensure that you don't need to re-allocate when pushing items to it. There's been significant benchmarking done for vector resizing costs and it's wildly situational
general rule of thumb, only optimize when its a bottleneck, if it doesnt cause lag on the potato pc laptop of your friend, then it is not a problem like for example, the other day i was adding weapon upgrades to my game, here is how the code looks like(in pseudo code): *way later in class initialisation* public int gun_upgrades if gun_upgrades >= 30: *shoot max upgrade shot* else if gun_upgrades >= 28: *shoot max upgrade shot with one upgrade missing* else if gun_upgrades >= 26 *shoot max upgrade shot with a different upgrade missing* ... you get the point right? this code is ran every time the player presses the shoot button, but that only happens at most once every 10 frames because of a fire rate cap, and so, why bother optimizing right? this is probably never going to be a problem, more likely the code *inside* the if/else blocks is going to be more performance intesive than this monstrosity but i did do some high level optimizations in here, i know that instancing 5 different instances of the projectile class at the same time is going to possibly create a lag spike, and so, i am not doing that, instead what i am doing is that when a projectile hits something, it plays a burst animation, and at the end of that burst animation, it has its hitbox and physics disabled, and it is turned invisible, so it might as well not exist, and when you fire a new projectile, it just loops through every projectile, finds the first one that is in that limbo state, reinitialises it, and boom, its done!
when playing final fantasy 7, i found myself thinking "hm i wonder if this game has any super crazy low level optimizations", but the fact of the matter is that there is most likely none of that because compilers are just so good at optimizing for you, after all, its the main reason we use compiled languages afaik
I agree. I think one reason that people do this is that people find it fun to go into micro-optimisations, and doing low level bit hacking. However, it doesn't really matter that much. It's 2024, not 1985, and both compilers and computers are really good
Probably a retarded idea, but when you talked about iterating over the block, it quickly popped into my mind - storing the map in a map :D In a sense of having map data structure where the key is the block type, and value is array or set of the block instances. In my beginner brain, it seems like that would be the best idea to check collisions against blocks of specific types.
But what about optimization by just using STL algorithms everywhere you can. Personally when I write code and remember that there's a perfect STL function that solves my problem i rather use it, even though it may seem hideous and not simple at all, but I believe that people behind their implementations know their stuff. Also while writing a medium/large scale project I found myself not designing it because I thought it doesn't matter as long as things work, which in the end lead me to redisigning it and trying to tie everything together because I was just sick of it. I believe that having this "premature optimisation" mindset is beneficial in making those good design choices in the beginning, which will make project just write itself later. I know that I'm missing the point of this video with this one, but I know that I've been on both sides of the mindset and it's not so clear when it's okay to be pessimistic and "prematurely" optimize and not okay.
i am doing a lot of this early optimisation: Right now i am refactoring some code that has 3 deep loop. On my machine with my current test-data it is only ~200ms ... sadly i know that the real system have in excess of 5000 elements - rather than my 3. And i'd rather have code i can read than this giant 500 line kraken with continue/return statements sprinkled throughout. But darn - somebody already complained to me that i would be wasting memory cause i included 3 !!! copies of an ID-value in the function..... yes, 3 64bit variable in a function that does n^3 computation on >5000 complex (dependency graphs) objects.
So I want to talk about the example function at the beginning of the video. Beyond optimization I don't think its ever a good idea to remove that length check from the comparison, even if you know that a comparison will fail if it goes beyond the length of the original string, we dont have guarantees on how the string will be used inside of an actual system such as if some one uses something that isnt a sub string of the original string this could cause all kinds of errors. also you don't need i1 and i2 they are always the same values when doing comparison so it just a waste to have two, not for performance reasons but because it makes it unclear what your doing
now with the unsigned int part of the code I am a bit confused about how it ended up being only 200ms, if this is caused by overflow errors then the expected behavior would be that if it overflows it would loop forever and would never exit the for loop because they would end up checking the same spot over and over since in this case the roll over behavior would it would go from its max value back to 0 and count back up But I also wonder how accurate this video is at all, punching in the same code into compiler explorer I almost always get less instructions in second version of compare than the first, which normally is a good sign that one piece of code is faster.
There is also a lot wrong with the code other than the optimizations, rather than unsigned int size_t should be what is used which I believe is a int long long, because this fits the behavior pattern for what computers recognize as on of the longest piece of data that you can have on that computer architecture. along side that the function comparison 1 returns false if the two strings are the same, which is the opposite of what most people expect, then there is the if(ret != 0) return ret >0 this line is mostly pointless we know ret is greater than 0 just return true no need for the extra logic, once again this probably doesn't change speed because the compiler will most likely remove it but you make what is happening less clear to the reader with unneeded logic
It's not when a button takes 2 seconds to react. Though most indie game devs don't need to optimize to the extreme, you should never be in the scenario where a 2d platformer can't run at 200fps
It's the exact thing many programmers today ARE subconsciously thinking LOL. If something like Windows explorer takes longer to open than it takes to lift your finger from the button, it shows programmers are being lazy and over reliant on hardware.
10:26 1. char is NOT guaranteed to be signed by default in C. For example, on ARM it is conventional to use unsigned char. 2. It is conventional to use unsigned chars on ARM because unsigned math is faster. On x86(_64) they are the same speed but with signed you get more optimization opportunities. 3. 255 already overflows a signed char (assuming char is 8-bit).
Default "char" is a non-numerical type the compiler assumes is being used for characters, it is neither signed nor unsigned in that context. Specifying "signed" or "unsigned" tells the compiler that it is being used as an 8bit numerical type ("char" can be defined as something other than 8bits for special hardware, but typically it is 8bit). Of course it is more clear and more portable if you use int8_t or uint8_t for exactly 8bit numbers. This will throw a compiler error on hardware that does not use 8bit bytes but that may be desirable. (Or for maximum hardware portability [u]int_least8_t , or [u]int_fast8_t . The latter will generally result in 32bits on x86 and 64b on amd64, 8b on 6502 or ATmega, 16b on 80286, and 31b on IBM370; 12b, 18b, 24b, 36b, 45b, and 48b have also been used in commercial products (18 and 36 were popular in the 1960s with packed 6bit chars).
@@mytech6779 This distinction you've made between "numerical" and "character" types is not something that exists in the C standard, or any C compiler. "char", "signed char", and "unsigned char" are all both integer types and character types.
@@mytech6779 Quoting C99/N1256 6.2.5 paragraph 15: "The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char." And 6.2.5 paragraph 17: "The type char, the signed and unsigned integer types, and the enumerated types are collectively called integer types." "An InTeGeR tYpE iS nOn-NuMeRiCaL"🤣 And so the compiler cannot assume it is only used for characters. Also yes, signed and unsigned are relevant in this context. I'm sure I can find an older official source, but this is the oldest one that is easy to find.
@@hemerythrin sure 8 bits is 8bits, there isn't even a physical difference between a signed and unsigned char in memory. However it is hinting for the compiler and being distinct means a future compiler&machine could treat them different. ISO C standard, arithmetic types: char - type for character representation. Equivalent to either signed char or unsigned char (which one is implementation-defined and may be controlled by a compiler commandline switch), **but char is a distinct type, different from both signed char and unsigned char. ** Note that the standard library also defines typedef names wchar_t , char16_t and char32_t(since C11) to represent wide characters and char8_t for UTF-8 characters(since C23). ********** The ISO C++ standard also considers them distinct types, along with a few extra variations: signed char - type for signed character representation. unsigned char - type for unsigned character representation. Also used to inspect object representations (raw memory). char - type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type). Multibyte characters strings use this type to represent code units. For every value of type unsigned char in range [0, 255], converting the value to char and then back to unsigned char produces the original value.(since C++11) The signedness of char depends on the compiler and the target platform: the defaults for ARM and PowerPC are typically unsigned, the defaults for x86 and x64 are typically signed. wchar_t - type for wide character representation (see wide strings). It has the same size, signedness, and alignment as one of the integer types, but is a distinct type. In practice, it is 32 bits and holds UTF-32 on Linux and many other non-Windows systems, but 16 bits and holds UTF-16 code units on Windows. The standard used to require wchar_t to be large enough to represent any supported character code point. However, such requirement cannot be fulfilled on Windows, and thus it is considered as a defect and removed. char16_t - type for UTF-16 character representation, required to be large enough to represent any UTF-16 code unit (16 bits). It has the same size, signedness, and alignment as std::uint_least16_t, but is a distinct type. char32_t - type for UTF-32 character representation, required to be large enough to represent any UTF-32 code unit (32 bits). It has the same size, signedness, and alignment as std::uint_least32_t, but is a distinct type. (since C++11) char8_t - type for UTF-8 character representation, required to be large enough to represent any UTF-8 code unit (8 bits). It has the same size, signedness, and alignment as unsigned char (and therefore, the same size and alignment as char and signed char), but is a distinct type.
@@marbens Physically the computer has no clue what a type is let alone a class of numbers or character encoding. It is just some combination of wires with high and low voltages that doesn't mean anything to the machine. Running a program is just a gigantic cascading chain reaction of switching transistors following some initial state. How that state is set up to get the desired output does depend on the method of interpreting the bits to mean a character or number and what sort of encoding is used for either. You can stuff a couple chars and an int the floating point unit on a CPU and it will spit out a result just the same as if you fed it two floats, it won't be a generally useful output but the machine can't tell the difference. Types are used entirely by the compilers to formulate rational machine instructions.
What many miss is that it can never be an optimization if you don’t measure it. If you don’t measure an improvement under normal expected use, then it’s not an improvement.
Does having whole OOPS structure with singletons and App.run() like thing hurt performance? because it looks really unnecessary to me to make it whole OOP and it looks as if we're making it harder for the compiler to understand it and optimize it
it is making it harder for the compiler but since it is a small thing the performance hurt won't be visible. However it is a case of code pesimization and I don't do things like that because as you said they are unnecessary and bring absoljtely no benefit to my code so don't do it 💪
You're wrong Optimization isn't the root of all evil _Anything_ is the root of all evil _Cries in rewriting the engine 3rd time in a row cuz it's a mess every time I look at it_ Also funny thing about optimization: I had a function that draws a 20x20 box in CMD, it took just over 1ms to render Simple two loops to iterate over vertical and horizontal boundaries and draw them Then I made a 360d Line Drawing algorithm and replaced the function with it, expected for it to be slower The time decreased to 0.5ms for the same box.
doing abstractions over your code is also a form of optimization, but you are optimizing your code reusage instead of the code speed. And it can lead to having to rewrite your stuff. Also for the CMD stuff, the cmd is painfully slow unofrtunatelly. 1ms is an eternity but cmd is very slow so idk what happened there. Maybe you added an endline instead of ' ' that can indeed make it slower. idk but it can't be because of the loop iteration
The code sample shown at 0:15 looks wrong to me. The "optimized" version doesn't have a proper abort condition and is accessing memory outside a const* string when the strings are identical to the zero byte (at which the memory allocation ends).
It's not even in seconds, it's in kilo-seconds. If it was seconds, it'd be 0.0045, but there are 3 extra zeros. I'm thinking they divided it by 1000 when they should've multiplied it.
@@jmvr good point lol. I think you are correct in regards to the division. I said seconds due to delta time usually being in “seconds” 0.0004 seconds as an example.
@@davidcauchi I can see what you mean, because I thought the same thing when I read your comment. That's when I noticed just how long the decimal value was, because last time I checked 60fps was 0.016ms, which is much larger than 0.000005 lol
Is there no bounds checking being done on the strings? What the? I had to go look at the code from this book and this looks so broken to me. If I'm looking at the right chapter anyway. I see it's returning false from the Compare1 function to signify that the strings are not equal as indicated by the if (s1 == s2) line to compare pointers to check if both are not pointing to the same object. So false = strings are equal If res is not 0 meaning the return from the inner compare function (which I do not see the code for) concluded the characters being compared were different. So why not just do "return true;" instead of "return res > 0;"? Removes the unneeded comparison. Are these strings passed in guaranteed to be equal length? Even with that now that I think about it, there's no way to terminate the loop... You're going to get Access Violations/Seg faults with this code. Oh, you don't need two counters for the index. Just use one counter. Could probably do something like while (*s1++ == *s2++) anyway.
Here's my version: bool compare(const char* s1, const char* s2) { //if strings are pointing to the same memory if (s1 == s2) return true; //as long as we haven't hit the end of either string while (*s1 != '\0' && *s2 != '\0') { if (*s1++ != *s2++) //if they're different, return false return false; } // If we reached the end of both strings, they are the same // if we only reached the end of one, then they're different return *s1 == '\0' && *s2 == '\0'; }
yess, but with code pesimization, in c# since the language is garbage collected you might need to do extra things to make sure that doesn't cause you problems and you don't allocate too much memory.
Some of it yes, some of it no The general rules of not wasting time on small details and figure out if what you are doing would actually help remain true But because c# is much higher level it comes with a lot of stuff you would have to make yourself in lower level languages pre implemented, functional and already well optimized it is often easier to write clean and optimized code
this optimization he is showing, is a incorrect optimization, correct optimization is a optimization that improves speed rather than size, and improves efficency of a slow thing
i mean is it not better to optimize it from the start. minecraft was a simple game with not that much blocks entities and other things. So there was no reason to optimize it. But now Mincraft much more complex and they will never optimize it good because it just way more harder than before.
>beginners dont prematurely optimize and waste ur time >btw beginners do wut casey muratory tells u to do kinda extremely contradictory also c++ not having garbage collection doesnt make it faster in any way, garbage collection isnt inherently slower than raii or freeing memory manually
i wish to not many people see this comment
too late, I pinned it, for now at least 😌
please explain this to me ! pls
@@random_userk123Yeah, I too got pin shamed under Mox’s video on bees. However I am shameless about my dirty deeds :3
@@random_userk123 the person leaving the comment is being sarcastic, and they *do* want people to see the comment. people will see it and like the comment so more people see it, thus "defeating" the person who left the comment.
@@TheMCEnthusiastPlays WAIT! you are saying that this person is an attention grabber?! *gasps in shock and horror*
Who could ever do such a evil thing?! He MUST be punished for it!
Ok, but also find guy with slow old laptop, and make sure that game is also fast enough on his laptop with limited resources, and if is not, then you should try to optimize
Okey, I'll leave my 2006 PC with an Core 2 Duo and 2gb of ram with a Nvidia GForce 210 to you.
Treat my lil boi nicely
@@joakotorres402 minecraft launched in 2009, you should try optimise your game around that unironically.
Let me dig up my ancient PC that has 1 gig of ram and an integrated gpu. You better optimize the game good so I can run it.
Let me charge my 8 year old Samsung Galaxy J2
You better make an optimized android port of your game so it can run it
It depends on the game.
But if you're making a simple 2d action platformer, or a card battler/TCG, or a top down roguelike etc. then your game should absolutely run fine on like a 2008 computer, and if it isn't maybe you should optimize it
I prematurely optimize not to achieve better performance, but because that's my default coding style. Example:
- constexpr when possible;
- allocate objects on stack when possible;
- prefer std:array over std::vector and std::vector over other containers, unless container is large and elements needs to be removed from begin/middle;
- reuse heap allocated objects when possible; e.g. network read/write buffer;
- one line member functions define in it's class definition, so it would be inlined;
- large objects or objects who allocates memory on heap internally, pass around as const &/*;
- RVO/NRVO;
- if needed to loop over large amount of data, maybe use struct of arrays (SOA) instead of array of structs (AOS);
- avoid interfaces when possible;
Of course i would not sacrifice code readability for questionable performance gains.
I'm optimizing RIGHT NOW and there's nothing you can do about it! 😏
Profilers are underrated
you're like a father to me at this point with your amount of actual handy advice as more or less a noob
glad to help bro 💪💪💪💪 I am happyto get comments like this
I would definitely agree that performance is not the cause for concern with that crafting system. In my eyes, it's how time consuming and error prone manually entering all the different variants of a single recipe will get.
I find writing the project out on paper and doing proper planning before starting saves a lot of headaches. Writing tests and benchmarks also helps before committing code, it's a bit more hassle but worth it.
Insulating newbies from the idea that optimization is important will let them form bad habits that will hard to break
Teach noobs to use efficient algorithms and data structures, then worry about micro optimization later
THIS!
Thank you, I always begin to optimize my code before it's even done and working, it happens against my will and have to stop myself every time
Me too. It's fun at first, but then you spent your time doing that and now you don't have anything that works, and that's not fun.
The first example you show isn't an optimization that "ended up being slower"... It's a bug that surprisingly didn't crash the program lol. It's slower because it's checking past the end of the array into what is now other data, but still treating it as the previous type....
His point was to profile, it didnt matter why it mattered that it wasnt measured
@@Dimblar the profile is entirely pointless if it's not measuring against the same control...
Obviously a loop will take longer if it's checking more items; which is exactly what's happening. The ONLY way that the profile means anything at all, is if the early return condition is at the same index within the array for both checks... Which is CLEARLY not the case, as the profile took longer when it is obviously doing less work.
Take a dynamically allocated, null-terminated string (so the compiler can't optimize the loop exit condition/unroll), and use the null terminator as the only exit condition - and it WILL end up being faster.
What's the point of a misleading example?
@@Hazanko83 It is not a misleading example, the point is to profile or measure the code with a tool not eyeball it. This control and solution is not going to be known to beginners, so the solution is to do as I have just said.
@@Dimblar It's like trying to record the speed of a cheetah, but you throw it off a cliff.
It's not measuring what you think it is.
I'm more than familiar with profiling, and wrote a custom profile that I can litter through my game engine where I need it (it's not hard). If beginners look at this code, and think it's an actual optimization, they're going to be really confused why they can't get a stable program running.
Optimizations will always be faster, or they wouldn't be optimizations... that's why it's hard to show an example where it fails. If he's trying to teach beginners in the method of "don't care about the details just listen", then he shouldn't show code that will 100% crash your program, and he shouldn't show profiles that are measuring two completely different things and showing them side by side.
Either he's aware he's showing a contrived and dangerous example and just needed filter for the video(as an opening nonetheless), or he's unaware and any good teacher wouldn't want to be teaching something incorrect.
You have not watched the video till the end have you. The code in question is run on a substring and is therefore evaluated the same
For me it’s you should optimise things that are called very very often like multiple times per second
As for the other part of code being called way less often just make sure it works forget optimise.
once the project it’s finished you can start doing more heavier optimisation
yes that is a good approach. Don't forget tho that even if something is called often, it might still be fast enough
A few years back I got back into some game development for the first time in 30 years.
I was amazed at how inefficient for example Unity and Godot actually are as frame works, but PCs became perhaps 4000x faster in the 34 years since i programmed my last game on a Atari ST 68000 8MHz parts I had even written in assembly to keep the speed.
It almost feels freeing to make a side scroller shmup and just do everything in the game loop without trickery :D Especially when I used Zig and Raylib, no added game engine overhead. 1200+ fps on a Mac Book m1 :D It's insanely fast without "optimisation"
Although I have an innate urge to optimise especially for memory usage, that comes as second nature to me. Having programmed my first game on a 3KB VIC20 :D But it's trivial to have a well performing 2D game these days.
What bothers me is that Unity is a big company that can afford to perform optimizations but choose not to. More features = better to them at the cost of building on top of deprecated everything. I am stuck in the Unity landscape at work because nobody wants to get their hands dirty and make their own engine.
Golden rule of optimization, or software development even, is: "Solve problems which you have, instead of problems you might have"
yess, idk why it isn't obvious
Bro, your minecraft clone is beautiful.
I'm glad you included the first type and the third type for contrast. Some early structural choices can limit later options, important operations in a game may be very latency sensitive but low throughput, versus some program that does scientific number crunching which is not affected by latency but requires high throughput per joule.
Even selecting the language can be considered an initial optimization yet could not be considered premature. Language selection could also be in the pessimization category, if you start with a slow highly abstracted language then writing the prototype code may be much faster and have fewer obvious bugs, but eventually you may have complications when optimizing.
You should also talk about FPS. People very often ignore improvements like 10 from 11 FPS (90ms difference), but panic when uncapped game goes from 600 to 500 (0.3 ms difference). And other common problems like running all logic every frame along rendering, not using tools like Nvidia Insight to see what is actually going haywire under the hood via frame debugger, and not resetting chrono properly (start value taken right before while loop, end value including cost of outputting all debug info), etc
Agreed.
Professionals measure frame time in milliseconds (or even nanoseconds.)
Amateurs measure FPS which becomes *more and more inaccurate the higher the frame rate.*
This is the most convoluted rust w I ever heard
10:55 the best variables for loops are size_t
bro missed the point completely 😂😂 use whatever type you want for a loop
Yes, but I decided to simply pull out a pointer variable declaration from a loop into the parent scope and suddenly it’s 12% faster, even with -O3 and vectorization enabled. This loop had a nested loop, and I was executing 7,998,000 2D AABB collision checks every run, and I had such things like ‘__restrict’ on my pointers. I thought it was a fluke so I retested several times, and then I reordered the tests to run the slow one first and the fast one second, yet the result was the same. I was using Clang 16.0.5 at the time.
nicee, well you clearly measured your code there and it is justified so I approve your method 💪
Well you did measure it which is fine in that case
"Premature Optimization" does not exist!
There is only:
Proactive Optimization
- designing things effectively before you start building
Reactive Optimization
- refactoring tested parts to make them perform as expected
Unnecessary Optimization
- optimizing things that don't need to be optimized, or things that you don't need at all
Aim for proactive, be prepared for reactive, avoid unnecessary.
Okay but the last point is universally recognized to be what premature optimization is referring to
One time I refactored all my code base by replacing every i++ with ++i
One time I refactored all my code base by using const int& for every function arguments instead of just int
Yes, I also used std::list everywhere at the time lol
What if one of those i++ actually had a purpose to be there? Maybe the code relied on the fact that it returns value before it was incremented?
@@hoardingapples7083 No. Absolutely never lol
I felt that in my soul. I once relied on ++i to check that i hit the end of an array and then give a "value not found" error. It's the little weird stuff that'll mess with overall function @hoardingapples7083
Yep very true, people like to argue on which piece of code is faster but almost never measure it's performance, and also like to say stuff "oh if this function gets called 1 million times it's gonna be slow". I think it's called "Bike Shedding". The reality is that sometimes people forget that they need to optimize code after they measure that's it's actually slow.
I'm unhinged fanatic when it comes to code optimization, but I still don't waste my time on optimizing code that doesn't need it.
I use this mentality when I'm coding:
1. ooga booga working code first
2. if code is too slow, I measure which part of it is slow (this is very important) and optimize it (and fix bugs, ugly parts etc)
3. repeat first step
Also sometimes it takes few iterations of code to make it fast and working, like face culling code for example, it was easy for me to make it fast, but it was producing wrong results
Very good video btw👍
The simpler and clearer you write code for people, the easier it is for the compiler to optimize this code (but not always). Before any optimizations of processor or memory usage, do a benchmark, evaluate the contribution of costs against the overall background, and only then proceed with optimizations.
Im not a developer by any means but having to deal with grass caches in skyrim mods and constantly updating it each time a plugin touches it just to add "free" fps makes your point sooo true and valid
A key philosophical underpinning here is immediate mode vs retained mode design. I have increasingly valued the former.
yes I love immediate mode design
To state or not to state, that is the question.
i was making big integer that has no limits in size, like in python. And in this case reserving memory before doing push_backs was really important. But this project was focused in optimization from the start because big integer has no limits. But some fast algorithms like Karatsuba's multiply was slower than simple algorithm so keep it simple
If Jonathan Blow saw this video he would be mad
hm, I think he would actually agree with it
jonathan blow only whines about everything.
No, actually Jon expressed similar thoughts in one of his speeches:
th-cam.com/video/JjDsP5n2kSM/w-d-xo.htmlm34s
Lmao he would not be mad over this he would totally agree
I had to learn this the hard way and it scarred me for life...
This is correct. Unfortunately, beginners are not able to distinguish between optimization and non-pessimisation. So I would recommend that they don't attempt either of them. If you are still learning C/C++, just write code that works and that you can understand.
I actually just spent the last two days, un-optimizing a part of my game engine, for the sole purpose of encouraging future localization.
The hit to performance is pretty minimal, and it actually makes all of my game's data files, slightly smaller, as a nice bonus.
interesting story 💪💪
This is why planning is key. If the plan calls for only 3 items, there’s no point making some generic optimised inventory system that can support a million items and run in O(1) time
1. Program for correctness.
2. Profile.
3. If you need more performance, focus on what is taking the most CPU.
4. Do not sacrifice correctness.
5. Keep the correct version around for a programmer to read to see what is meant to be happening.
6. If reading the optimised code is not clear, then use comments to explain both what it's meant to do, what optimisations you've applied, and so on so that a programmer reading it can easily work out what's going on rather than having to try to decode the kind of cryptic mess that optimisation usually results in.
For example doom was revolutionary in 3d fps, when it came, pc didn't have even 1/10 of todays pc power, and they didn't even do much optimization, but this game is still game in the hall of fame
esti foarte bun, chiar dupa 2 ani de cpp si 1 de python inca mai am de invatat de la tine (is baiatu cu geamu)
Tiny, tiny optimisations are a happy coder problem
FPS as a perf measurement.... Please please don't.
Indeed. The reason we measure *frame time (in milliseconds)* and NOT FPS is because the higher the FPS the more inaccurate it gets.
Когда я начинал делать игру на FNA, я думал, что заниматься оптимизацией мне придётся ещё не скоро, ведь игра то простенькая.
И очень удивился, когда увидел 15 fps.
Это 2д top-down игра с процедурной генерацией. Мир в ней генерируется полностью за раз. Так вот, я сделал генерацию и всё было в порядке, однако, стоило мне немного увеличить мир, как fps просаживался. Я понял, что в игре отрисовываются даже те тайлы, которые не видны на экране, поэтому решил сделать так, чтобы отрисовывались только те, что должны быть видны. Я это сделал, это сработало. Однако... Когда я был не дома, хотел поработать над игрой на своём ноутбуке, который гораздо слабее моего основного ПК. Тогда я заметил, что, fps хоть и повышается, когда я отрисовываю только нужные тайлы, всё равно, чем больше мир, тем менее плавно работает игра. Какое-то время я не мог понять, в чём дело. Потом до меня дошло озарение, что виноват цикл, перебирающий абсолютно каждый тайл, чтобы проверить, близко ли он к камере.
Я начал думать, что можно сделать, и решил разделить мир на чанки. (Что забавно, я не стал менять способ генерации, а просто начал разбивать на чанки уже сгенерированный мир)
Сначала я планировал уменьшить количество переборов циклом. Мир 1000 на 1000 с чанками размером 10 на 10, перебирался бы куда меньшее количество раз, если перебирать чанки. Но потом я понял, что от лишних переборов можно вообще избавиться, если по позиции камеры понять, в каком она чанке. Так я начал отрисовывать только чанк, в котором камера, и близлежащие к нему чанки.
Это позволило поднять fps в мире 10.000 на 10.000 с 2, до 2000.
Ха-ха, конечно, я думаю видео немного о другом, потому что мои проблемы были вызваны большой неопытностью, но всё-таки, мне хотелось поделиться своей историей с оптимизацией 😃
Upd: после этой истории я понял, почему в Майнкрафте используют систему тиков, лол
Eco-unfriendly games designed to run OG doom at 60fps on modern hardware lmaooo.
Be careful with pre-sizing vectors. Vectors are pretty well optimized, even when resizing is required
hm idk what to say about this. If you have let's say 1000 elements to push back and your vector isn't pre sized, it will visibly lag your game. It is probably the first optimization you can do
@@lowlevelgamedev9330 For new vectors yes, if you're re-using vectors however their allocators significantly overprovision memory to ensure that you don't need to re-allocate when pushing items to it. There's been significant benchmarking done for vector resizing costs and it's wildly situational
One time I wrote:
struct {
unsigned int a: 16;
unsigned int b: 16;
}
keeping code simple is often an optimization in and of itself
It's not about the maturity of the optimization, it's about the quality of the "optimization". (Aka algorithm design, and its quality)
based alllman style programmer
general rule of thumb, only optimize when its a bottleneck, if it doesnt cause lag on the potato pc laptop of your friend, then it is not a problem like for example, the other day i was adding weapon upgrades to my game, here is how the code looks like(in pseudo code):
*way later in class initialisation* public int gun_upgrades
if gun_upgrades >= 30:
*shoot max upgrade shot*
else if gun_upgrades >= 28:
*shoot max upgrade shot with one upgrade missing*
else if gun_upgrades >= 26
*shoot max upgrade shot with a different upgrade missing*
...
you get the point right?
this code is ran every time the player presses the shoot button, but that only happens at most once every 10 frames because of a fire rate cap, and so, why bother optimizing right? this is probably never going to be a problem, more likely the code *inside* the if/else blocks is going to be more performance intesive than this monstrosity
but i did do some high level optimizations in here, i know that instancing 5 different instances of the projectile class at the same time is going to possibly create a lag spike, and so, i am not doing that, instead what i am doing is that when a projectile hits something, it plays a burst animation, and at the end of that burst animation, it has its hitbox and physics disabled, and it is turned invisible, so it might as well not exist, and when you fire a new projectile, it just loops through every projectile, finds the first one that is in that limbo state, reinitialises it, and boom, its done!
This video makes sense
I get it but I really wanna save assembly lines on a damn 0.1 Hz computer compiler, so I will optimise it. I WANT OPTIMISE COMPILER
when playing final fantasy 7, i found myself thinking "hm i wonder if this game has any super crazy low level optimizations", but the fact of the matter is that there is most likely none of that because compilers are just so good at optimizing for you, after all, its the main reason we use compiled languages afaik
I agree. I think one reason that people do this is that people find it fun to go into micro-optimisations, and doing low level bit hacking. However, it doesn't really matter that much. It's 2024, not 1985, and both compilers and computers are really good
Probably a retarded idea, but when you talked about iterating over the block, it quickly popped into my mind - storing the map in a map :D In a sense of having map data structure where the key is the block type, and value is array or set of the block instances. In my beginner brain, it seems like that would be the best idea to check collisions against blocks of specific types.
well depends in the context. It is fast but you usually don't need that and as mentioned in the video just iterating through all the map is very fast
The time code with the name of the book is 9:30
But what about optimization by just using STL algorithms everywhere you can. Personally when I write code and remember that there's a perfect STL function that solves my problem i rather use it, even though it may seem hideous and not simple at all, but I believe that people behind their implementations know their stuff.
Also while writing a medium/large scale project I found myself not designing it because I thought it doesn't matter as long as things work, which in the end lead me to redisigning it and trying to tie everything together because I was just sick of it. I believe that having this "premature optimisation" mindset is beneficial in making those good design choices in the beginning, which will make project just write itself later. I know that I'm missing the point of this video with this one, but I know that I've been on both sides of the mindset and it's not so clear when it's okay to be pessimistic and "prematurely" optimize and not okay.
This video was awesome!!! Also, im doing texture arrays. When i test it on one computer it works but on the other the textured dont show up
i am doing a lot of this early optimisation:
Right now i am refactoring some code that has 3 deep loop. On my machine with my current test-data it is only ~200ms ... sadly i know that the real system have in excess of 5000 elements - rather than my 3. And i'd rather have code i can read than this giant 500 line kraken with continue/return statements sprinkled throughout.
But darn - somebody already complained to me that i would be wasting memory cause i included 3 !!! copies of an ID-value in the function..... yes, 3 64bit variable in a function that does n^3 computation on >5000 complex (dependency graphs) objects.
but, find memory leaks early
So I want to talk about the example function at the beginning of the video.
Beyond optimization I don't think its ever a good idea to remove that length check from the comparison, even if you know that a comparison will fail if it goes beyond the length of the original string, we dont have guarantees on how the string will be used inside of an actual system such as if some one uses something that isnt a sub string of the original string this could cause all kinds of errors.
also you don't need i1 and i2 they are always the same values when doing comparison so it just a waste to have two, not for performance reasons but because it makes it unclear what your doing
now with the unsigned int part of the code I am a bit confused about how it ended up being only 200ms, if this is caused by overflow errors then the expected behavior would be that if it overflows it would loop forever and would never exit the for loop because they would end up checking the same spot over and over since in this case the roll over behavior would it would go from its max value back to 0 and count back up
But I also wonder how accurate this video is at all, punching in the same code into compiler explorer I almost always get less instructions in second version of compare than the first,
which normally is a good sign that one piece of code is faster.
There is also a lot wrong with the code other than the optimizations, rather than unsigned int size_t should be what is used which I believe is a int long long, because this fits the behavior pattern for what computers recognize as on of the longest piece of data that you can have on that computer architecture.
along side that the function comparison 1 returns false if the two strings are the same, which is the opposite of what most people expect,
then there is the if(ret != 0) return ret >0
this line is mostly pointless we know ret is greater than 0 just return true no need for the extra logic,
once again this probably doesn't change speed because the compiler will most likely remove it but you make what is happening less clear to the reader with unneeded logic
To add, computers can only go so fast. If you over optimize, what are you even doing with all those extra cycles in your super small scope game?
Good advice
great video. "a pc is goddamn fast" is a quote programmers tend to forget
not really... everything bloated electron today
It's not when a button takes 2 seconds to react. Though most indie game devs don't need to optimize to the extreme, you should never be in the scenario where a 2d platformer can't run at 200fps
Other than triple a devs that is.
It's the exact thing many programmers today ARE subconsciously thinking LOL.
If something like Windows explorer takes longer to open than it takes to lift your finger from the button, it shows programmers are being lazy and over reliant on hardware.
And that is why we can afford to write stuff in javascript instead of C++....
10:26
1. char is NOT guaranteed to be signed by default in C. For example, on ARM it is conventional to use unsigned char.
2. It is conventional to use unsigned chars on ARM because unsigned math is faster. On x86(_64) they are the same speed but with signed you get more optimization opportunities.
3. 255 already overflows a signed char (assuming char is 8-bit).
Default "char" is a non-numerical type the compiler assumes is being used for characters, it is neither signed nor unsigned in that context. Specifying "signed" or "unsigned" tells the compiler that it is being used as an 8bit numerical type ("char" can be defined as something other than 8bits for special hardware, but typically it is 8bit).
Of course it is more clear and more portable if you use int8_t or uint8_t for exactly 8bit numbers. This will throw a compiler error on hardware that does not use 8bit bytes but that may be desirable. (Or for maximum hardware portability [u]int_least8_t , or [u]int_fast8_t . The latter will generally result in 32bits on x86 and 64b on amd64, 8b on 6502 or ATmega, 16b on 80286, and 31b on IBM370; 12b, 18b, 24b, 36b, 45b, and 48b have also been used in commercial products (18 and 36 were popular in the 1960s with packed 6bit chars).
@@mytech6779 This distinction you've made between "numerical" and "character" types is not something that exists in the C standard, or any C compiler. "char", "signed char", and "unsigned char" are all both integer types and character types.
@@mytech6779 Quoting C99/N1256 6.2.5 paragraph 15:
"The implementation shall define char to have the same range,
representation, and behavior as either signed char or unsigned char."
And 6.2.5 paragraph 17:
"The type char, the signed and unsigned integer types, and the enumerated types are
collectively called integer types."
"An InTeGeR tYpE iS nOn-NuMeRiCaL"🤣
And so the compiler cannot assume it is only used for characters.
Also yes, signed and unsigned are relevant in this context.
I'm sure I can find an older official source, but this is the oldest one that is easy to find.
@@hemerythrin sure 8 bits is 8bits, there isn't even a physical difference between a signed and unsigned char in memory. However it is hinting for the compiler and being distinct means a future compiler&machine could treat them different.
ISO C standard, arithmetic types:
char - type for character representation. Equivalent to either signed char or unsigned char (which one is implementation-defined and may be controlled by a compiler commandline switch), **but char is a distinct type, different from both signed char and unsigned char. **
Note that the standard library also defines typedef names wchar_t , char16_t and char32_t(since C11) to represent wide characters and char8_t for UTF-8 characters(since C23).
**********
The ISO C++ standard also considers them distinct types, along with a few extra variations:
signed char - type for signed character representation.
unsigned char - type for unsigned character representation. Also used to inspect object representations (raw memory).
char - type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type). Multibyte characters strings use this type to represent code units. For every value of type unsigned char in range [0, 255], converting the value to char and then back to unsigned char produces the original value.(since C++11) The signedness of char depends on the compiler and the target platform: the defaults for ARM and PowerPC are typically unsigned, the defaults for x86 and x64 are typically signed.
wchar_t - type for wide character representation (see wide strings). It has the same size, signedness, and alignment as one of the integer types, but is a distinct type. In practice, it is 32 bits and holds UTF-32 on Linux and many other non-Windows systems, but 16 bits and holds UTF-16 code units on Windows. The standard used to require wchar_t to be large enough to represent any supported character code point. However, such requirement cannot be fulfilled on Windows, and thus it is considered as a defect and removed.
char16_t - type for UTF-16 character representation, required to be large enough to represent any UTF-16 code unit (16 bits). It has the same size, signedness, and alignment as std::uint_least16_t, but is a distinct type.
char32_t - type for UTF-32 character representation, required to be large enough to represent any UTF-32 code unit (32 bits). It has the same size, signedness, and alignment as std::uint_least32_t, but is a distinct type.
(since C++11)
char8_t - type for UTF-8 character representation, required to be large enough to represent any UTF-8 code unit (8 bits). It has the same size, signedness, and alignment as unsigned char (and therefore, the same size and alignment as char and signed char), but is a distinct type.
@@marbens Physically the computer has no clue what a type is let alone a class of numbers or character encoding.
It is just some combination of wires with high and low voltages that doesn't mean anything to the machine. Running a program is just a gigantic cascading chain reaction of switching transistors following some initial state. How that state is set up to get the desired output does depend on the method of interpreting the bits to mean a character or number and what sort of encoding is used for either.
You can stuff a couple chars and an int the floating point unit on a CPU and it will spit out a result just the same as if you fed it two floats, it won't be a generally useful output but the machine can't tell the difference.
Types are used entirely by the compilers to formulate rational machine instructions.
What many miss is that it can never be an optimization if you don’t measure it. If you don’t measure an improvement under normal expected use, then it’s not an improvement.
this is my mindset hahahahah 6:54
Does having whole OOPS structure with singletons and App.run() like thing hurt performance? because it looks really unnecessary to me to make it whole OOP and it looks as if we're making it harder for the compiler to understand it and optimize it
it is making it harder for the compiler but since it is a small thing the performance hurt won't be visible. However it is a case of code pesimization and I don't do things like that because as you said they are unnecessary and bring absoljtely no benefit to my code so don't do it 💪
Do you use a library for your Minecraft clone like SFML? afaik, SFML is just for 2D so do you use a 3D library?
nope, it is just OpenGL. A 3D library doesnt help you with a project like mc because you kinda need to write the pipeline yourself
Optmization is not for computers, is for programers.
You're wrong
Optimization isn't the root of all evil
_Anything_ is the root of all evil
_Cries in rewriting the engine 3rd time in a row cuz it's a mess every time I look at it_
Also funny thing about optimization: I had a function that draws a 20x20 box in CMD, it took just over 1ms to render
Simple two loops to iterate over vertical and horizontal boundaries and draw them
Then I made a 360d Line Drawing algorithm and replaced the function with it, expected for it to be slower
The time decreased to 0.5ms for the same box.
doing abstractions over your code is also a form of optimization, but you are optimizing your code reusage instead of the code speed. And it can lead to having to rewrite your stuff.
Also for the CMD stuff, the cmd is painfully slow unofrtunatelly. 1ms is an eternity but cmd is very slow so idk what happened there. Maybe you added an endline instead of '
' that can indeed make it slower. idk but it can't be because of the loop iteration
Shrimply dont
Not really an option in rust
The code sample shown at 0:15 looks wrong to me. The "optimized" version doesn't have a proper abort condition and is accessing memory outside a const* string when the strings are identical to the zero byte (at which the memory allocation ends).
look at the part of the video where I explain it. It is wrong in the general case yes but it was used on substrings of the same string
2:40 something is up with the ms value here. 240 fps should be around 4.1ms correct? Looks like it’s in seconds not ms.
I say that because ms is a much better metric imo. But bigger reason is I fixed the same issue in my code yesterday haha.
It's not even in seconds, it's in kilo-seconds. If it was seconds, it'd be 0.0045, but there are 3 extra zeros. I'm thinking they divided it by 1000 when they should've multiplied it.
@@jmvr good point lol. I think you are correct in regards to the division. I said seconds due to delta time usually being in “seconds” 0.0004 seconds as an example.
@@davidcauchi I can see what you mean, because I thought the same thing when I read your comment. That's when I noticed just how long the decimal value was, because last time I checked 60fps was 0.016ms, which is much larger than 0.000005 lol
Is there no bounds checking being done on the strings? What the?
I had to go look at the code from this book and this looks so broken to me. If I'm looking at the right chapter anyway.
I see it's returning false from the Compare1 function to signify that the strings are not equal as indicated by the if (s1 == s2) line to compare pointers to check if both are not pointing to the same object. So false = strings are equal
If res is not 0 meaning the return from the inner compare function (which I do not see the code for) concluded the characters being compared were different.
So why not just do "return true;" instead of "return res > 0;"? Removes the unneeded comparison.
Are these strings passed in guaranteed to be equal length? Even with that now that I think about it, there's no way to terminate the loop...
You're going to get Access Violations/Seg faults with this code.
Oh, you don't need two counters for the index. Just use one counter. Could probably do something like while (*s1++ == *s2++) anyway.
Here's my version:
bool compare(const char* s1, const char* s2) {
//if strings are pointing to the same memory
if (s1 == s2) return true;
//as long as we haven't hit the end of either string
while (*s1 != '\0' && *s2 != '\0') {
if (*s1++ != *s2++) //if they're different, return false
return false;
}
// If we reached the end of both strings, they are the same
// if we only reached the end of one, then they're different
return *s1 == '\0' && *s2 == '\0';
}
Is the things that you say also true in c#?
yess, but with code pesimization, in c# since the language is garbage collected you might need to do extra things to make sure that doesn't cause you problems and you don't allocate too much memory.
Some of it yes, some of it no
The general rules of not wasting time on small details and figure out if what you are doing would actually help remain true
But because c# is much higher level it comes with a lot of stuff you would have to make yourself in lower level languages pre implemented, functional and already well optimized it is often easier to write clean and optimized code
Keep that optimizing for AI
this optimization he is showing, is a incorrect optimization, correct optimization is a optimization that improves speed rather than size, and improves efficency of a slow thing
use Rust and optmize everythingg, thats what i do
i mean is it not better to optimize it from the start. minecraft was a simple game with not that much blocks entities and other things. So there was no reason to optimize it. But now Mincraft much more complex and they will never optimize it good because it just way more harder than before.
depends on what you optimize. If you optimize important things yes but beginners make bad choices
Did you even watch the video bro
>beginners dont prematurely optimize and waste ur time
>btw beginners do wut casey muratory tells u to do
kinda extremely contradictory
also c++ not having garbage collection doesnt make it faster in any way, garbage collection isnt inherently slower than raii or freeing memory manually
Usually when comparing GC against non-GC, people compare a GC that solves memory fragmentation against a non-GC variant which does not.
Also: quit trying to do the compiler’s job
there are still people tho whose job is to do that tho. The compiler isn't perfect
nevermind my first comment