Announcement

Collapse
No announcement yet.

file_LoadFunction memory allocation

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • file_LoadFunction memory allocation

    In my application I am using a number of small functions, which are dynamically loaded from SD using file_LoadFunction. What I have noticed is that the memory allocation for the loaded function is equal to the actual size of the function + approximately 570 bytes. This overhead is more or less constant, regardless of the function size. This is really a annoying, because with the limited RAM size, the overhead is often much larger than the size of the loaded function.

    I ASSUME that the 570 byte overhead is created during the actual reading of the file from SD and contains the one-sector buffer for opening and reading the file. After the file is read, another heap allocation is created after the file buffer and filled with contents of the function file. The file buffer is (presumably) not required any more (there is no need for further reads from the file), but the heap memory can not be released, because there is another heap entry after the file buffer (the actual function).

    My suggestion would be to modify the file_LoadFunction in such a way, that the file buffer is released from the heap and contents of the loaded function are shifted by 570 bytes over the file buffer (a kind of forced garbage collection). I am not sure if this is possible, but the release of those 570 bytes would really be a great improvement, even at the expense of some extra processing.

    Regards,
    Valentin

  • #2


    The heap is reporting the 'maximum chunk size' available.
    When you load a function, the 570 bytes is allocated first at the top of the heap
    (the heap works downwards), then the function(s) sit below it.
    The 570 bytes is re-used, then released each time a function is loaded, or some other
    disk operations may use it.
    If you were to allocate some memory at this point, eg:
    var chunk;
    chunk := mem_Alloc(560);

    You will see that the reported heap doesn't change, but you were given that memory that was
    left up top.

    Any form of garbage collection to overcome this would not be feasible, the system would need
    to keep track of all pointers, and adjust them accordingly (the way Basic does it)
    Regards,
    Dave

    Comment


    • #3

      Hi Dave,

      I don't get the behavior that you are describing. It appears that new heap entries are always appended at the bottom of the heap and the 570 byte buffer is not reused. Furthermore, if I create a memory allocation (560 bytes), create another memory allocation (10 bytes) and free the first memory allocation, this should create a gap in the heap chain, which should be available for new allocations. However, if I add another small memory allocation, it is again placed at the bottom of the heap and not in the large gap that was just created. I think this is actually the correct way, because filling gaps in the heap will result in somewhat undeterministic behavior. On the other hand, if your explanation is correct, then there might be a bug in the heap management.
      I have written a short example to illustrate the behavior. The three function files can contain any code and be of any size, but they have to be located on the SD card.


      #platform "uLCD-32PT_GFX2"

      func main()
      var tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, heap;
      gfx_ScreenMode(4);
      while(!file_Mount()) putstr("SD?");

      print(mem_Heap(),"\n"); //Initial heap size, prints 13812
      heap:=mem_Heap();
      tmp1:=file_LoadFunction("AVRCFS.4FN"); //small function file, 188 bytes
      print(mem_Heap()," ",heap-mem_Heap(),"\n"); //new heap size 13046, heap decrease 766=188+578
      heap:=mem_Heap();
      tmp2:=file_LoadFunction("AVRCFA.4FN"); //small function file, 61 bytes
      print(mem_Heap()," ",heap-mem_Heap(),"\n"); //new heap size 12408, heap decrease 638=61+577
      heap:=mem_Heap();
      tmp3:=file_LoadFunction("AVRCFE.4FN"); //small function file, 40 bytes
      print(mem_Heap()," ",heap-mem_Heap(),"\n"); //new heap size 11790, heap decrease 618=40+578
      heap:=mem_Heap();
      tmp4:=mem_Alloc(560); //large memory block allocation
      print(mem_Heap()," ",heap-mem_Heap(),"\n"); //new heap size 11226, heap decrease 564=560+4
      heap:=mem_Heap();
      tmp5:=mem_Alloc(10); //small memory block allocation
      print(mem_Heap()," ",heap-mem_Heap(),"\n"); //new heap size 11212, heap decrease 14=10+4
      heap:=mem_Heap();

      mem_Free(tmp4); //release the large memory block

      tmp6:=mem_Alloc(10); //small memory block allocation
      print(mem_Heap()," ",heap-mem_Heap(),"\n"); //new heap size 11198, heap decrease 14

      repeat
      forever

      endfunc




      Regarding the missing 570 bytes in the file_LoadFunction, I still believe that this issue can be solved within the file_LoadFunction. I agree that once the pointers are set, there is no reasonable way that you can reallocate them correctly. However, I suggest that the function code is reallocated before any pointers are generated. Here is the suggested algorithm:
      1. create the 570 byte buffer on the bottom of the heap
      2. open the function file on the SD card
      3. copy the content of the function file to the new memory block on the bottom of the heap
      4. close the function file on the SD card
      5. release the 570 byte buffer from the heap
      6. move the memory block containing the function code up the heap for 570 bytes. At this point I assume that the only pointer that would have to be modified is the heap pointer containing the address of the memory block on the heap.
      7. Generate any other code and data pointers, memory allocations for local and global variables, etc.

      As you can see I practically suggest only the addition of step 6. Step 7 would remain the same, it would just operate on a memory block at different physical memory address. The changes would have to be made within the file_LoadFunction by upgrading the PmmC, because once the step 7 is completed, changing all the pointers is practically impossible.

      Regards,
      Valentin

      Comment


      • #4


        Thanks for your further information.

        Testing using your demo code does indeed show up a problem.

        Unfortunately there is no workaround at the moment.

        The PmmC has now been modified accordingly...

        file_LoadFunction now has the following behaviour at the slight expense of increased load time.

        1] open file
        2] read header
        3] close file, releasing FCB and buffer
        4] make memory allocations based on header requirements
        5] Open file again (creating FCB and buffer below the functions memory allocation)
        6] seek past header
        7] load code segment to the allocation
        8] close file, releasing FCB and buffer

        This also has the positive side effect of leaving no holes anywhere, and all function allocations
        are nested consecutively (top down) as expected without missing any memory.

        PmmC version 3.3 will be released withiin the next few days.
        Regards,
        Dave

        Comment


        • #5


          Excellent!

          I am looking forward to test the new PmmC. I frequently load as much as 5 or 6 functions at the same time, so this should boost my application with 3k additional available RAM

          Regards,
          Valentin

          Comment


          • #6


            I finally got some time to test the fixed PmmC and it works perfectly, the memory allocations sizes match the size of the loaded file .

            I have also checked the speed and as expected, it is considerably slower. For a small function (size < 512 bytes) I used to get about 4.8 ms, now it is about 12.1 ms. Each additional sector (extra 512 bytes) takes another 2.5 ms in both versions. This is a rather large increase in loading time, but I find perfectly acceptable, because the gain in free RAM is also large.

            However, I have noticed another interesting thing. The the above timing is valid only for the first 16 files on the SD card (first sector in the FAT table). The next 16 files will have and additional loading time of approximately 4.7 ms. This appears to be caused by the way that files are open using FAT system. The system first loads the first sector in the FAT table and checks the first 16 files. If no match is found, it loads the next sector and so on until it founds a match.

            There really is not much that can be done about this, but the loading speed can be optimized by the user in a very simple way. The most time critical files should always be loaded to the SD card first, as this will put them in the beginning of the FAT table. If there are many files on the SD card, the gain can be quite significant. In the worst case, if the SD card contains 510 files and the loaded file is in the last position, the loading time can increase by as much as 150 ms...

            Regards,
            Valentin

            Comment


            • #7


              Yes unfortunately thats the nature of the FAT file system, it would be difficult to make it work more efficiently.
              What would be needed is a system that works the way the Image Control works (pre-loaded cluster/sector addresses in a table so no seeking is required).

              Unfortunately there would be a lot of work in that, and then there are the management headaches of
              making it 'programmer friendly'
              Regards,
              Dave

              Comment

              Working...
              X