Demo: Temporary Storage, Expandable Context, Placeholders

แชร์
ฝัง
  • เผยแพร่เมื่อ 12 ก.ย. 2024

ความคิดเห็น • 75

  • @123hgf456
    @123hgf456 7 ปีที่แล้ว +44

    Jai is really shaping up into a bad ass language, can't wait until we can use it!

  • @superkool7
    @superkool7 7 ปีที่แล้ว +6

    Jonathan Blow.....you truly are the man! One of the most brilliant minds of our time. From programming to design and everywhere in between. Thanks for all of your amazing work and please keep it coming!

  • @64jcl
    @64jcl 6 ปีที่แล้ว +1

    The integrated temporary storage is a very nice feature! In many other higher level languages that has garbage collection one have to be dead careful and preallocate stuff or make elaborate recycling schemes so the VM doent go bananas trying to garbage collect everything and cripples the CPU. The simple allocator solution that is reset every frame is a perfect use for this since it hardly uses any CPU to allocate a memory block and can safely be overwritten every frame. Looking forward to be able to work with this language some day.

  • @vladimirsemyonov2848
    @vladimirsemyonov2848 7 ปีที่แล้ว +2

    I saw some interesting parallels between the "temporary storage" and how GHC (Haskell compiler) manages memory. AFAIK, it also allocates by simply bumping a pointer in "temporary" region of memory. Then, during the GC, it copies only the necessary/"live" values to a different region of memory (compacting them in the process), and then discards everything that was allocated in the "temporary" of memory.
    Here it looks kind of the same (if you squint really hard), except, of course, you have to ensure the data you really need "lives" by manually copying it. And the usability benefits are similar.

    • @jblow888
      @jblow888  7 ปีที่แล้ว +3

      Sure, this is how all generation scavenging collectors work. The difference here is that, as you say, the programmer needs to copy something in order for it to stay live -- but the expected use is that you probably don't bother to copy things almost ever, because you used a different allocator for things you know need to live a long time.

  • @KytoDragon
    @KytoDragon 7 ปีที่แล้ว +1

    I have now implemented a similar temporary storage for my game engine and I have to say I love it. It makes using short lived memory so much easier as you don't have to constantly keep references to stuff you have to free after one function call. (I am already using ~200k per frame, without get_ and set_temporary_storage_mark)

    • @jblow888
      @jblow888  7 ปีที่แล้ว

      Wow, I would say if you are doing 200k of temporary storage per frame, that by itself may be a substantial optimization opportunity...

    • @KytoDragon
      @KytoDragon 7 ปีที่แล้ว

      The engine was originaly developed in a garbage colected language (not anymore), so a few parts still rely more on allocation than they should.

    • @KytoDragon
      @KytoDragon 7 ปีที่แล้ว

      A bit of optimisation on my level loader and the highest mark is down to 10k. Being able to directly see what allocated how much really helped getting it down.

  • @crybirb
    @crybirb 7 ปีที่แล้ว +19

    Damn I can't wait to try this. Is there a date for a beta or something?

  • @MrRobinjam
    @MrRobinjam 7 ปีที่แล้ว +5

    Completely off-topic but... what keyboard do you use? I like how it sounds.

    • @jblow888
      @jblow888  7 ปีที่แล้ว +6

      It's a keyboard that uses Cherry MX Blue switches; you can find these in many brands / styles / etc.

  • @hugoviala7365
    @hugoviala7365 7 ปีที่แล้ว

    Hey Jon ! Thanks for keeping us up to date with the language. I am pretty excited and cannot wait to try it out.
    I had a question which is not directly linked to this video and which might even seem pretty minor. But you stated several times that one goal of Jai is to provide a seamless experience to the programmer. Therefore, how do you feel about imposing a specific naming convention (like_this_convention or MaybeThisConvention) to the user when they make use of the standard library of the language ? Do you even think this is an important problem for a compiler to consider ?
    Thanks a lot and good luck with this awesome project :)

    • @jblow888
      @jblow888  7 ปีที่แล้ว +1

      I think it is important to let people use the style they like.

    • @hugoviala7365
      @hugoviala7365 7 ปีที่แล้ว

      Yes but doesn't it go against the particular style of a standard library (in Jai's example, it_would_be_this_style_that_you_are_imposing) where you are indeed forcing people to use one style for some part of their program ?

    • @jblow888
      @jblow888  7 ปีที่แล้ว +5

      I don't know what you mean.
      The goal of this language is to solve hard problems, not to get particular about things like style.
      If I name procedures in a library InCamelCaseLikeThis, and someone doesn't like that, they are actually free to import the library with a renamer proc that transforms the names to whatever they want.

  • @androth1502
    @androth1502 5 ปีที่แล้ว

    why not use hybrid strings? in the hla language/standard library, a string has a structured header. the string type is actually a pointer that starts at the zero-terminated string data, at -8 you can find the memory pointer/max length, at -4 is the current length. the library made use of these strings and if needed to interact with a c-string library, just pass the string pointer and since it's zero-terminated, the c-lib can handle it without any conversions.

  • @Chrisosaurusrex
    @Chrisosaurusrex 7 ปีที่แล้ว +1

    I'm a big fan of the context for allocation and logging, does all printing go to that logger?
    One problem I often have in C programs is trying to use my own allocator and logging strategy with code that wasn't written to be generic across different allocators, ends up involving diving into the code to refactor seriously.
    C++ stringstreams were a first step along this, as at least they give you a way of using strings as if they were streams.
    I think the contextual logger is the next step along, I've seen dynamic languages often 'hack' this into the background - I'm very interested in seeing where this can go as a language feature.

  • @mx0r
    @mx0r 7 ปีที่แล้ว +2

    interesting concept with this temporary storage; as for the syntax, what about something like:
    temp found: [..] *Entity;
    It would be definitely more readable than /t

    • @nameguy101
      @nameguy101 7 ปีที่แล้ว +1

      That's dumb. You've moved the temporary indicator not just out of the array designation, not just out of the entire type, but before the damn declaration, along with reserving the incredibly commonly used identifier temp. All so it can be "readable".

    • @mx0r
      @mx0r 7 ปีที่แล้ว

      ok, but why is it a bad thing actually? you need to indicate that certain block scoped variable will be stored in temporary storage; otherwise, it behaves just like any other block scoped variable, so you don't need to care about it being stored in temporary storage.

    • @saniancreations
      @saniancreations 2 ปีที่แล้ว

      @@mx0r Hey there, here's your 5 years overdue explanation why it's a bad thing: Your keyword of choice, "temp" is very commonly used as a variable name. Reserving that keyword to only be used as syntactic sugar for something like this is restraining the programmer more than it's worth. That said, I think the *placement* of your keyword is actually a bigger problem that at first may not be so obvious.
      Jai is very specific about its syntax being clear in its meaning. Variable declarations strictly follow the pattern "name : type = value". You can see this approach in the syntax for explicitly not initializing variables, instead of something like "uninitialized name : type;" the syntax is "name : type = ---". This syntax very clearly shows that the *value* of this variable is uninitialized. With your suggestion "temp name : type;" it seems as if the temp keyword is saying something about the name-part, or maybe even the whole expression (similar to inline for example).
      When you think of it this way, even the syntax Jon showed as an example "array_name : [..\t] int;" seems questionable, because the type doesn't actually change when you assign the temporary allocator. It remains a dynamic array, what really changes is the *value* of the allocator *inside* the dynamic array. So in my opinion, syntactic sugar for declaring a dynamic array that uses the temporary allocator should be put after the assignment operator, to signify that this dynamic array has a special value, not that is has a special name or type, because it doesn't.

  • @xFrostbite94
    @xFrostbite94 7 ปีที่แล้ว +1

    Do you plan on including some sort of "local pointer" type? I.e. a specific type of pointer that can only point to temporary memory? Don't want to take it too far (sounds like something rust would do), but it would make obvious errors statically detectable (e.g. assigning a local pointer to a normal pointer variable is probably not a good thing).

    • @jblow888
      @jblow888  7 ปีที่แล้ว +2

      I don't know. I don't want to get carried away with introducing lots of wacky types. But something like this could maybe be done with straightforward extensions of the relative pointer stuff, that are going to happen anyway. But I think stuff like this has to be left up to individual developers who want to play with the concepts they think will be useful ... some things are too speculative to force on everyone.

  • @Chainelove
    @Chainelove 7 ปีที่แล้ว +5

    when the temporary storage is full and it fails over to the default allocator you could save that somewhere to then free it when you ask for a reset of the temporary buffer
    not ideal but to me a memory leak is unacceptable
    what do you think ?

    • @jblow888
      @jblow888  7 ปีที่แล้ว +10

      Not worth the complication. A correctly-running program should never fill in the first place; that is the whole point!

    • @dosboot1
      @dosboot1 7 ปีที่แล้ว +3

      Perhaps I don't know, but is it really that complicated for something that is a bona fide language feature? The complication only has to be paid once, and everyone who uses the language gets to benefit by having it available. I think people feel uneasy enough to ask for it because being literally correct requires making sure there is a maximum allocation and knowing how to the stress the program during testing to reach it (or to get within a fudge factor).
      I admit, it does feel like a narrow sequence of events has to happen for the benefit to be realized... that the programmer missed something truly significant, and some users run into a specific scenario with a big enough leak that their computer can't handle it.
      Then again, why have the fallback allocator at all in the release build? That too is just allowing some users who run into specific/overlooked scenarios from having a problem. From the perspective of a language user (i.e. who isn't considering the cost of an already implemented feature) they'd want to have both features enabled (the fallback allocator and a "fallback free"), correctness responsibility or not.

    • @skyloftian8241
      @skyloftian8241 7 ปีที่แล้ว

      Would there be any problems if instead of calling alloc when the buffer fills you instead alloc a new buffer and point to it from the first so successive overflows can use the same memory or have the memory freed at reset.
      // step 1. add this field to Temporary_Storage
      overflow_buffer: *Temporary_Storage;
      // step 2. replace "__temporary_allocator" with something like
      __temporary_allocator :: (requested_bytes: s32) -> *void {
      selected_buffer := *context.temporary_storage;
      while true {// the first if is usually all that happens
      if selected_buffer.occupied + requested_bytes selected_buffer.high_water_mark
      selected_buffer.high_water_mark = selected_buffer.occupied;
      return result;
      }
      else if selected_buffer.overflow_buffer != null { //try the next buffer if it exist
      selected_buffer = selected_buffer.overflow_buffer;
      }
      else { //log error, make a new buffer, and point to it
      logger("temporary_buffer overflow!!", indent, Log_Mode.MINIMAL, logger_data);
      new_size := max(context.temporary_storage.size, requested_bytes);
      selected_buffer.overflow_buffer = alloc(sizeof(Temporary_Storage) + new_size);
      selected_buffer = selected_buffer.overflow_buffer;
      selected_buffer.data = cast(*u8)(selected_buffer + sizeof(Temporary_Storage));
      selected_buffer.size = new_size;
      selected_buffer.occupied = 0;
      selected_buffer.high_water_mark = 0;
      selected_buffer.overflow_buffer = null;
      }
      }
      }
      // step 3.
      option 1. make reset_temporary_storage set the occupied field in each buffer to 0 (without freeing).
      option 2. make reset_temporary_storage free all the extra buffers and set the overflow_buffer to null.
      (edit) option 3. leave reset_temporary_storage alone since it will still work normally when the buffer is unfilled
      option 4. add a buffers_used field to Temporary_Storage and make a buffers_traversed_count in the func
      then set the occupied field to 0 if you go a buffer beyond the buffers_used and inc buffer_used
      there and when a new buffer is created, then make reset_temporary_storage set buffers_used to 0

    • @jblow888
      @jblow888  7 ปีที่แล้ว +2

      There would not be any "problems", this is just not the intended usage of the allocator. If you want an allocator that does what you are talking about, you can make that easily. However, it is not something that I think is appropriate for the core language, so it is best to leave that as a user-level thing.

    • @skyloftian8241
      @skyloftian8241 7 ปีที่แล้ว

      What I was trying to say was, if you still log the error and the programmer still records the high_water_mark in order to prevent temporary_storage from filling in the first place (under normal circumstances), what would the cost(problems) be, in terms of performance and added complexity, of failing in a safer way.
      In reply to the original comment, you said that it's not worth the additional complexity. So at what point is the solution simple enough to be worth it?
      You said that it's best to leave that sort of thing to user level but unless I'm misunderstanding the demo, unlike the context allocator the temporary_allocator isn't part of the context so it's not possible to change the temporary_allocator that libraries use (is this possible with metaprogramming?).
      So right now if one were worried about a library leaking through temporary_storage they would have to
      a. avoid all libraries that use temporary_storage
      b. manually or semi-automatically calculate the worst case scenario temporary_storage usage of all the library's functions
      or
      c. just live with the risk of memory leaks, which may not be that big of the deal since you can get_mark then set_mark and add a safety margin over the high_water_mark but it still feels like a problem to me because if the program ever reached a state where it leaked memory, one would expect the neighboring states to have similar behavior and leak on every frame until the user leaves the vicinity of the initial leak state (i.e they leave the room that uses extra temporary storage while in the mode that uses extra storage while having 5 inter-connected AI agents spawned when they usually have 2)

  • @Chainelove
    @Chainelove 7 ปีที่แล้ว +9

    39:32 there is a typo in GL.jai if the OS is not Windows
    winow : Window;
    shouldn't it be
    window : Window;

    • @jblow888
      @jblow888  7 ปีที่แล้ว +13

      Caught in the Q&A!

  • @cas1652
    @cas1652 6 ปีที่แล้ว

    it would be nice if the temporary allocator (or the context) had mark() reset() already built in because otherwise programs will have this all over the place in an adhoc fashion (like here in the entity drawing)

  • @Chainelove
    @Chainelove 7 ปีที่แล้ว +17

    result := covfefe; Kappa

  • @ajones0916
    @ajones0916 7 ปีที่แล้ว +1

    In your experience how has debugging been in a language with so much compile-time code generation? Do you find it easy to reason about the state of your program at run-time or do you find that mistakes in the generated code are hard to track down?
    If so, I wonder if this could be solved by tooling (editor /IDE plugins which show expanded code and/or in-source compile-time functionality that simplifies generating / displaying / debugging a resolved code state).

    • @jblow888
      @jblow888  7 ปีที่แล้ว +2

      It is mostly not any different, especially because there is text for generated code. So far generated code is a small percentage of the total, and that's likely to be the way it is for any sane codebase. But we'll see how it goes, it's early days still!

    • @ajones0916
      @ajones0916 7 ปีที่แล้ว +1

      Yeah, it makes sense that limited code generation is best for the average app / game. With Jai's powerful compile-time execution though I can imagine cases where broad code generation could be very useful (a SQL alchemy style ORM library for example). Regardless, I really love the frequent demo / QA uploads and I'm excited to get my hands on it whenever that may be. Keep up the good work!

  • @Xavier-cd6fx
    @Xavier-cd6fx 6 ปีที่แล้ว

    A small question about the c string conversion, why don't you always size the string with the extra \0 character? In this way no conversion will be required and should work without a copy when passed as const char*.

  • @DelusionalLogic
    @DelusionalLogic 7 ปีที่แล้ว +1

    Is there a reason "push_context" gets special treatment? It seems like it could be accomplished with a variable and and a defer, in fact you do that in other similar cases.

    • @nameguy101
      @nameguy101 7 ปีที่แล้ว

      It couldn't, because procedures always pass context _implicitly._ Allowing explicit context passing is more dangerous than push_context, which remains simple.

  • @Chrisosaurusrex
    @Chrisosaurusrex 7 ปีที่แล้ว

    currently your reset temporary buffer resets the used count and the water mark together, you track the water mark as a global - this makes the water mark in struct redundant.

    • @jblow888
      @jblow888  7 ปีที่แล้ว

      Not correct. See the part where I set and reset the mark.

    • @Chrisosaurusrex
      @Chrisosaurusrex 7 ปีที่แล้ว

      Jonathan Blow I think the reset temporary function should do that recording and set for you, rather than requiring the user to do it, otherwise it doesn't really need to be on the struct.

    • @jblow888
      @jblow888  7 ปีที่แล้ว +1

      You are missing something. There are two separate things that I demo. One is a full reset. The other is a rollback of the allocated size to an earlier position.
      If you roll back at some point before you reset, then obviously the count is not equal to the high water mark. Thus the high water mark is necessary.

    • @Chrisosaurusrex
      @Chrisosaurusrex 7 ปีที่แล้ว

      It's possible I'm missing something.
      At 5:30 you show reset_temporary_store :: () { set_temporary_storage_mark(0); context.temporary_storage.high_water_mark = 0; }
      I think here instead of setting the high water mark to 0 it would be better to perform the max and set as you show at 7:13

    • @Chrisosaurusrex
      @Chrisosaurusrex 7 ปีที่แล้ว

      For the bump allocator I understand how if you dealloc then your current alloc count won't reflect how many actual allocations you do, but I thought a high water mark was only needed to check your maximum usage - that is I think the high water mark should always contain max(higher_water_mark, storage_mark)

  • @bpunsky
    @bpunsky 7 ปีที่แล้ว

    Why would your context be polluted by libraries? The context fields should be contained to each library (workspace based on JAI's structure if I recall correctly) and if you need library functionality in your context, you could add it yourself. Libraries could even provide a struct full of all the stuff they'd like you to put in your context. I can't think of any circumstance where you'd want a library's local context stuff to carry over into user code, but I could be missing something.

    • @jblow888
      @jblow888  7 ปีที่แล้ว

      The whole point of a context is that it gets passed from procedure to procedure. If you say that there are different-sized contexts for different procedures, now invisible copies are going to be happening every time you pass stuff to a procedure, which sucks. And also, you would not be able to pass information into some library via the context, which is probably 80% of the reason that you want libraries to be able to add stuff to the context in the first place.

  • @KytoDragon
    @KytoDragon 7 ปีที่แล้ว

    Shouln't the temporary storage functionality be a standart library feature using the new custom context feature?

    • @jblow888
      @jblow888  7 ปีที่แล้ว +6

      No, because the whole point is that it always exists, so everyone can use it.

  • @anonymousweeble2224
    @anonymousweeble2224 6 ปีที่แล้ว

    Anyone know where the original video on Context (or Context_Base) is?

  • @Billyrulz1
    @Billyrulz1 7 ปีที่แล้ว

    Where can I find your live streams? or do you only make videos?

  • @RomanZenka
    @RomanZenka 7 ปีที่แล้ว +3

    Do you also provide alloca() equivalent that'd do temp allocation on the stack?

    • @jblow888
      @jblow888  7 ปีที่แล้ว +5

      Not yet, but probably eventually.

    • @youkenez
      @youkenez 7 ปีที่แล้ว

      guess you could use that instead of the global temp storage in most places

    • @jblow888
      @jblow888  7 ปีที่แล้ว +1

      No, because alloca is not useful for very much. That is the whole point of the temp storage. I would expect use of alloca to be extremely rare (much more rare than in C).

    • @RomanZenka
      @RomanZenka 7 ปีที่แล้ว +1

      Alloca has two issues IMHO: 1) it lasts only while your function is running, so you cannot return temp stuff out to the caller. 2) stack grows downwards (right?) so with the temp storage allocator you could do some crazy stuff like start writing over the memory and only after you are done realize how much you consumed and only then mark it as taken (?? not sure if this is good idea, probably only extremely rare and advanced use cases might benefit)

    • @youkenez
      @youkenez 7 ปีที่แล้ว

      of course...i was just thinking about getting rid of the frame-based nature of the temp storage but you & @Roman are right, alloca won't help. However, temp-storage could manage its free-space using a stack-based algorithm. Allocation would be fast (O(1)), freeing would be mostly O(1), worst O(log n) (binary search for marking blocks free in the stack of allocated blocks).

  • @rationalcoder
    @rationalcoder 7 ปีที่แล้ว

    Hey Jon, I work at a company that is clueless about performance with regards to allocation and multi-threading, and I have written a few different types of allocators like you would for games, but I'm not entirely sure how to write simple benchmarks that show exactly how much better it is to use these kinds of allocators. They don't have any kind of profiler setup(I'm working on getting one for them), so I need to write a telling unit-test/script to show them the difference since I can't just use there actual applications.
    Clearly, simply timing new/delete vs the allocators is going to show very small differences. What is a simple multi-threading scenario that I can setup in a unit-test to trigger worst-case behavior, and how do I time it correctly?
    Specifically, I am working in an embedded ARM Linux environment, and I have written chunk and stack allocators.

    • @jblow888
      @jblow888  7 ปีที่แล้ว +4

      I am not that big a believer in "unit tests", so I can't say I have any specific advice in that regard.
      The performance that matters is how quickly the deployed application runs. If they don't have any kind of profiling in place, that would be where to start ... you might well find that the biggest slowdowns don't even have anything to do with memory allocation, but something else entirely that is totally dumb and fixable in 10 minutes. (This happens often!)
      So if you want to get them to start caring about performance ... a priori suggesting solutions like changing allocators, etc, is not the right way to go. You want to measure first. Once you measure, then you can start to see what to do.
      If there is some question of triggering worst-case behavior .. I think you have to do that in the actual application. That is not a "unit test", it is a test of the full system. In games when we want to reproduce behavior to find logic bugs, we ensure that our code is deterministic and run it with a known random seed so that the same events occur every time (this is also how I debug the compiler). If there are input events, we log those inputs and add a mode so that we can run off the logged inputs instead of actual user input.

    • @rationalcoder
      @rationalcoder 7 ปีที่แล้ว

      Yea, I realize that getting a profiler up and running is the first step, it is just that I only recently gained enough say and respect to start dramatically changing the way they do things. A higher up came to me and asked me to fix some performance issues by handling allocation better. When I did so, but couldn't show big differences in performance, he kind of lost confidence in me. I mentioned from the start that not having a profiler setup before optimizing was ridiculous, but I think he just took that as me making excuses.
      At this point I'm just looking for a quick way to show that I did dramatically beat heap allocation with the allocators, so they will trust me when I setup a talk about introducing heavy refactoring, changing their build system, and introducing a profiler. Right now, I can show a 9X improvement over malloc by just timing it in a straightforward way, but that doesn't tell the whole story.

    • @jblow888
      @jblow888  7 ปีที่แล้ว +5

      It's not excuses. Having performance measurement set up is the *only* way to optimize. If you aren't measuring, you don't know what to optimize, so you can't optimize.

    • @rationalcoder
      @rationalcoder 7 ปีที่แล้ว

      Agreed. I guess I will just use my failure to show an isolated performance difference + time wasted doing it as a motivation for them to listen to me. Forgot to say nice vid, by the way.

  • @Arkume8Beltz
    @Arkume8Beltz 7 ปีที่แล้ว

    I'm sure if is posible simulate 4D in a VR 3D game, like we can draw 2D optical ilusion simulating 3D, sure you are the code wizard can do that..The Witness was amazing..
    The games can be a great tool to understand our minds without the limitation rules from our world..

    • @peterk822
      @peterk822 7 ปีที่แล้ว +4

      Miegakure

    • @g_glop
      @g_glop 7 ปีที่แล้ว

      Ardiak Beltz 4Dtoys.com (from the dev of Miegakure)

  • @APaleDot
    @APaleDot 7 ปีที่แล้ว +2

    46:00 result:= covfefe
    topkek