Shop OBEX P1 Docs P2 Docs Learn Events
Catalina - a self-hosted PASM assembler and C compiler for the Propeller 2 - Page 3 — Parallax Forums

Catalina - a self-hosted PASM assembler and C compiler for the Propeller 2

13

Comments

  • @RossH said:
    Some redesign of the assembler may be the only answer. Not Dave's fault, I should hasten to add - I am doing something with his code that he probably never envisaged.

    Maybe some small amount of debug profiling could help identify where exactly the slowness is. Recording how many data cache hits, misses and the total time taken waiting to read external data memory blocks into the cache vs overall execution time of the compilation process could be useful. Also some fine tuning of the number of cache rows and/or row size could alter the performance, and perhaps in its favour. The logged values may let you measure the improvements once you vary these things.

    Or maybe it's some sort of huge data structure initialization slowness and not the symbol table lookup algorithm itself. More detailed profiling could help home in on the really slow bits. Ideally you sort of want to build up frequency distribution of the data addresses of the cache misses so you can pinpoint what structure is the main culprit of all the thrashing, but that's a bit of work unless you just print everything for each miss, let it run as long as it needs and post process later. Could take ages I know.

  • RossHRossH Posts: 5,477

    @Wuerfel_21 said:
    I meant that even if every lookup misses the cache multiple times (they shouldn't) it still should be orders of magnitude faster than it apparently(?) is. Please read my post first.

    @Wuerfel_21 said:
    you could cache/evict a hundred cache blocks in that time.

    My attempt to explain the situation is no doubt to blame here. Let me try again ...

    The caching is known to work ok when it is mainly used for code access, where it is quite likely that the next code page required is already in the cache, so no PSRAM reads are required at all. For instance, if the code is in a smallish loop, all the pages in the loop will very end up in the cache after the first iteration, and once this happens the code must consult the cache every time it moves to a new page, but doing so does not result in a PSRAM read. This means you need no PSRAM reads at all most of the time, with code executing essentially at Hub RAM speeds, except for an occasional need to consult the cache, and a PSRAM read whenever the code moves on to a page that is not already in the cache.

    Let's assume each page contains about 100 instructions, and a PSRAM page load is required only on average every 10 page changes, and that doing a PSRAM page load takes about 1,000 instructions. So to execute 100 pages of code (i.e. 10,000 instructions) actually requires something like 100 x 100 + 10 x 1,000 instructions. So your code executes 10,000 instructions but it takes 20,000 instructions to do so - i.e. cached execution from PSRAM runs at 1/2 the speed of non-cached execution from Hub RAM (note that it is actually slower than that - this example just uses easy numbers).

    But if you then add a hash table which also uses the same cache, you are in trouble. A naive hash implementation could poison the entire cache on every hash table access - i.e. if the cache is small, the hash table is nearly full, and the hash function generates lots of collisions. This is because a hash table is designed to spread access across as much of the available memory space as possible. This means that every single hash table access might end up with all the cached code pages being replaced with hash table data pages as your hash function searches for the right entry. Which means that every time the code leaves the current code page, it will always need a PSRAM read to restore the code page, which has now been overwritten with a hash table data page.

    Assume that every page of instructions includes one or more hash table accesses. Then the code needs to load a code page by doing a PSRAM read on every page change, so to execute the same 10,000 instructions now takes 100 x 100 + 100 x 1,000 instructions, or 110,000 instructions. So the cached execution now runs at only 1/11 of the speed of non-cached execution - all because of the hash table.

    Add an order of magnitude slowdown because the P2 is simply not as fast as a PC (200Mhz instead of 2GHz) and another order of magnitude slowdown because file operations on an SD card are much slower than file operations on a hard disk.

    So it is easy to see how a disk-intensive process like a compile or assemble could end up 1000 times slower than the same process when run on a PC.

    Sure, some improvements are possible - but it will never be anything like as fast on a P2 as on a PC.

    Ross.

  • @RossH said:

    @"Christof Eb." said:

    Just being curious, I tried to follow this discussion. - Do I understand this right: Instead of linking preassembled object code, Catalina does the linking on source level and needs to recompile and/or reassemble the libraries every time?
    Christof

    Catalina compiles C to SPIN/PASM. The final PASM assembly is done after linking in all the library and run-time code.

    You have to remember that there were no tools available for the Propeller when Catalina was first developed other than a Spin compiler. There wasn't a language independent binary object format, and there wasn't (still isn't AFAIK) a stand-alone linker - there there wasn't even a stand-alone assembler - originally everything had to be done in Spin, because that was the only tool we had. This is still how Catalina works the Propeller 1, but on the Propeller 2 we at least had a stand-alone assembler developed fairly early by Dave Hein.

    Ross.

    Thank You for the explanation!
    I often think, that P2 with it's 512kB and with it's random access speed to HUB Ram of about 180/8= 23MHz is quite similar to early PCs or other computers of ~1984. Forth had reached it's zenith for PCs. I think Turbo-Pascal was then one of the most powerful or the most powerful IDE. It would be interesting to know, how this IDE worked internally. I assume, that one has to pull similar tricks as used in 1984 to get a system that is fun to work with.
    When I had a student's job programming a PDP11-23, this system had 1MB of Ram but could only use it as a RAM-disc. One of the methods, which was quite powerful, was to use overlays.
    I assume, that it is not soooo difficult to write a linker, if the modules are relocatable code, but it would be more difficult to have the assembler output relocatable object code.
    Christof

  • pik33pik33 Posts: 2,387
    edited 2023-06-01 09:13

    I think Turbo-Pascal was then one of the most powerful or the most powerful IDE.

    There was also Turbo C. And Turbo Pascal, and then then Delphi 1 for Windows 3.x was really powerful and fast

    I installed Windows 3.11 on a RPi (with Dosbox, so emulated machine was something like 10 - 30 MHz 386/486), and then Delphi 1 on it. A simple program, a form with a button that changes color when clicked, compiled and run near instantly, in less than a second... that was a shock, I forgot about how fast it was. Today's equivalent, Lazarus, is much slower on a 100x more powerful modern PC

  • @RossH said:
    But if you then add a hash table which also uses the same cache, you are in trouble. A naive hash implementation could poison the entire cache on every hash table access - i.e. if the cache is small, the hash table is nearly full, and the hash function generates lots of collisions. This is because a hash table is designed to spread access across as much of the available memory space as possible. This means that every single hash table access might end up with all the cached code pages being replaced with hash table data pages as your hash function searches for the right entry. Which means that every time the code leaves the current code page, it will always need a PSRAM read to restore the code page, which has now been overwritten with a hash table data page.

    But that should be the absolute worst case (or bad hash function or table too small) for an open hash table. If it's working properly the intended data should be either directly in the first slot checked or maybe a few slots down.

    Assume that every page of instructions includes one or more hash table accesses. Then the code needs to load a code page by doing a PSRAM read on every page change, so to execute the same 10,000 instructions now takes 100 x 100 + 100 x 1,000 instructions, or 110,000 instructions. So the cached execution now runs at only 1/11 of the speed of non-cached execution - all because of the hash table.

    But even if every hash table access somehow trashes the cache, that still wouldn't excuse the sheer extreme slowness.

    Add an order of magnitude slowdown because the P2 is simply not as fast as a PC (200Mhz instead of 2GHz) and another order of magnitude slowdown because file operations on an SD card are much slower than file operations on a hard disk.

    The SD speed should be way sufficient for the normal sort of operations. Even if you don't use multi-block, it's trivial to push over 1MByte/s of read speed. Single-block write is slow and bad though.

    Sure, some improvements are possible - but it will never be anything like as fast on a P2 as on a PC.

    Maybe the mere concept of a command taking longer than a minute just deeply offends me. But I am 100% convinced that you've got something not going right and that it would be a usable speed if that wasn't an issue.

    If one wanted it to be actually good instead of merely sufficient of course, it'd have to be aware of the tiered memory architecture, but C isn't really made for that.

  • evanhevanh Posts: 16,027

    To be fair, without the PC's refined high performance hardware caching capable of holding the entire data set, there would be huge performance hits with even the fastest SDRAMs. In theory, such hardware caching would work well with HyperRAMs too.

  • RossHRossH Posts: 5,477

    @Wuerfel_21 said:

    Maybe the mere concept of a command taking longer than a minute just deeply offends me. But I am 100% convinced that you've got something not going right and that it would be a usable speed if that wasn't an issue.

    If one wanted it to be actually good instead of merely sufficient of course, it'd have to be aware of the tiered memory architecture, but C isn't really made for that.

    I'm entirely content with "sufficient", and will release it when I think it achieves that :)

    Ross.

  • @RossH Not sure if you have control over the cache implementation being used or if you are relying on some existing code that has to work the way it does and doesn't know the difference between code and data accesses, but if you do manage this yourself then separating data rows from code rows may make sense to try. It can of course get tricky for any self modifying code where a D-cache write needs to invalidate a code page in the I-cache (or Von Neumann vs Harvard). If that situation is avoidable or unnecessary then managing the two separate caches could improve the situation quite a bit given sufficient hub RAM for both caches to fit. Code cache rows would not then be poisoned by the data accesses of the symbol table. Might result in a major speedup.

  • Wuerfel_21Wuerfel_21 Posts: 5,105
    edited 2023-06-01 20:53

    I've taken a bit of a look at the p2asm source (the version that comes with catalina, anyways). So, uh, it does this awful little loop to find keywords in FindSymbol:

        // Do case insensative search for PASM symbols
        for (i = 0; i < numsym1; i++)
        {
            if (StrCompNoCase(symbol, SymbolTable[i].name)) {
              return i;
            }
        }
    

    numsym1 is somewhere around 500 (would be more if it actually had all the newer predefined smartpin mode etc symbols). Each SymbolT is 108 bytes. Yea just casually scanning through some 54K for every symbol. This is directly on top of the hash table lookup. Why are these not handled through the hash mechanism? Also, these static symbols are initialized from a char*[] with some sort of awful sscanf construct. Cool code this is.

    EDIT: Looking further into it, wow I now hate whoever wrote this ;)

  • Wuerfel_21Wuerfel_21 Posts: 5,105
    edited 2023-06-01 21:37

    And to prove the point, here's a version where I moved the constant symbols into the main hash table (harder than it probably should've been), got rid of the sscanf table nonsense and also did some other minor optimizations. Still passes all the tests except the last one which was already broken before I changed anything.

    See if this one goes any better.

  • Unrelatedly, how would one place data in hub when compiling to XMM, the documentation doesn't say it, but does mention copying data from external memory to hub a few times, so I assume it's possible?

  • RossHRossH Posts: 5,477

    @Wuerfel_21 said:
    I've taken a bit of a look at the p2asm source (the version that comes with catalina, anyways). So, uh, it does this awful little loop to find keywords in FindSymbol:

        // Do case insensative search for PASM symbols
        for (i = 0; i < numsym1; i++)
        {
            if (StrCompNoCase(symbol, SymbolTable[i].name)) {
              return i;
            }
        }
    

    numsym1 is somewhere around 500 (would be more if it actually had all the newer predefined smartpin mode etc symbols). Each SymbolT is 108 bytes. Yea just casually scanning through some 54K for every symbol. This is directly on top of the hash table lookup. Why are these not handled through the hash mechanism? Also, these static symbols are initialized from a char*[] with some sort of awful sscanf construct. Cool code this is.

    EDIT: Looking further into it, wow I now hate whoever wrote this ;)

    Dave Hein wrote the original. I added a hash lookup to the main part of the symbol table, but didn't change the predefined keyword lookup (which is what the portion of the table below numsym1) represents) since it broke too many other things, and on a PC the overhead of this lookup is small compared to the overhead of looking up symbols in the main table, which can contain 30,000 - 40,000 entries for a large compile (e.g. the vi text editor, or Catalina itself).

    But as I said in a previous post, what you can easily get away with on a PC won't work on a P2.

    I'll try your modifications and also review the hashing strategy. Thanks.

    Ross.

  • RossHRossH Posts: 5,477
    edited 2023-06-02 03:35

    @rogloh said:
    @RossH Not sure if you have control over the cache implementation being used or if you are relying on some existing code that has to work the way it does and doesn't know the difference between code and data accesses, but if you do manage this yourself then separating data rows from code rows may make sense to try. It can of course get tricky for any self modifying code where a D-cache write needs to invalidate a code page in the I-cache (or Von Neumann vs Harvard). If that situation is avoidable or unnecessary then managing the two separate caches could improve the situation quite a bit given sufficient hub RAM for both caches to fit. Code cache rows would not then be poisoned by the data accesses of the symbol table. Might result in a major speedup.

    Yes, I am considering doing this. The problem is that it adds overhead to those things that currently work ok (i.e. code access) in order to improve some particularly poor cases of data access - so it may end up slowing down all programs for gains only in particular programs.

  • @RossH said:
    Yes, I am considering doing this. The problem is that it adds overhead to those things that currently work ok (i.e. code access) in order to improve some particularly poor cases - so you may end up slowing down all programs for gains only in particular programs.

    May still be worth a shot at least, just to find out. Hopefully it won't add too much extra code.

    With any luck you could leave the current instruction cache stuff alone and mainly add a secondary parallel path for data r&w accesses, or use a different entry point loading up other data structure pointers for that data lookup type before sharing the common cache logic implementation. But it's always harder than you first imagine though, I know...

  • RossHRossH Posts: 5,477
    edited 2023-06-01 23:30

    @Wuerfel_21 said:
    Unrelatedly, how would one place data in hub when compiling to XMM, the documentation doesn't say it, but does mention copying data from external memory to hub a few times, so I assume it's possible?

    In XMM SMALL mode, it is easy - all code is in XMM RAM, and all data is in Hub RAM.

    In XMM LARGE mode, C variables declared with block scope are in Hub RAM unless they are static, and C variables declared with either file scope or as static are in XMM RAM. All dynamic memory (i.e. allocated with malloc) is in XMM RAM. So if you want a C variable in Hub RAM, just declare it within a block or function but not as static. If you want it in XMM RAM, declare it with file scope or as static, or dynamically allocate it. Copying between Hub RAM and XMM RAM is always handled automatically - i.e. once you have declared it appropriately, you don't need to worry about it any further.

    If you are talking about raw memory, any memory address from 0 - 512k represents Hub RAM, otherwise it is XMM RAM.

    Ross.

  • @RossH said:

    @Wuerfel_21 said:
    Unrelatedly, how would one place data in hub when compiling to XMM, the documentation doesn't say it, but does mention copying data from external memory to hub a few times, so I assume it's possible?

    In XMM SMALL mode, it is easy - all code is in XMM RAM, and all data is in Hub RAM.

    In XMM LARGE mode, C variables declared with block scope are in Hub RAM unless they are static, and C variables declared with either file scope or as static are in XMM RAM. All dynamic memory (i.e. allocated with malloc) is in XMM RAM. So if you want a C variable in Hub RAM, just declare it within a block or function, but not as static. Copying between Hub RAM and XMM RAM is handled automatically.

    >
    So only stack allocation? Hmm.

  • @RossH said:
    Dave Hein wrote the original. I added a hash lookup to the main part of the symbol table, but didn't change the predefined keyword lookup (which is what the portion of the table below numsym1) represents) since it broke too many other things,

    Yea, there was this really obnoxious system where instructions with two opcodes (like JMP) needed to be consecutive in the table. Got rid of that by just putting the second opcode into value2

  • RossHRossH Posts: 5,477

    @Wuerfel_21 said:
    And to prove the point, here's a version where I moved the constant symbols into the main hash table (harder than it probably should've been), got rid of the sscanf table nonsense and also did some other minor optimizations. Still passes all the tests except the last one which was already broken before I changed anything.

    See if this one goes any better.

    Yes, thanks. This shaves a couple of minutes off each compile. The assembler is no longer the slowest step!

  • RossHRossH Posts: 5,477

    @Wuerfel_21 said:
    So only stack allocation? Hmm.

    Not sure what you mean. In XMM LARGE mode, all global variables, all static variables, and anything you allocate yourself are in XMM RAM. But local non-static variables are allocated on the stack - which is always in Hub RAM. This means they can be allocated and vanish automatically and at zero cost when the block is entered or exits. Managing the allocation and deallocation of local variables in XMM RAM would be painfully slow. If you really want variables to be in XMM RAM, you can either declare them as globals, declare them as locals but make them static, or dynamically allocate them.

    Ross.

  • RossHRossH Posts: 5,477

    @Wuerfel_21 said:

    @RossH said:
    Dave Hein wrote the original. I added a hash lookup to the main part of the symbol table, but didn't change the predefined keyword lookup (which is what the portion of the table below numsym1) represents) since it broke too many other things,

    Yea, there was this really obnoxious system where instructions with two opcodes (like JMP) needed to be consecutive in the table. Got rid of that by just putting the second opcode into value2

    Good work. I probably should have looked deeper, but who has the time? :)

  • @RossH said:

    @Wuerfel_21 said:
    So only stack allocation? Hmm.

    Not sure what you mean. In XMM LARGE mode, all global variables, all static variables, and anything you allocate yourself are in XMM RAM. But local non-static variables are allocated on the stack - which is always in Hub RAM. This means they can be allocated and vanish automatically and at zero cost when the block is entered or exits. Managing the allocation and deallocation of local variables in XMM RAM would be painfully slow. If you really want variables to be in XMM RAM, you can either declare them as globals, declare them as locals but make them static, or dynamically allocate them.

    Ross.

    The thing is, if I, say, have a PASM driver that needs some buffer to work in, where do I get that from?

  • RossHRossH Posts: 5,477
    edited 2023-06-03 02:22

    @Wuerfel_21 said:

    @RossH said:

    @Wuerfel_21 said:
    So only stack allocation? Hmm.

    Not sure what you mean. In XMM LARGE mode, all global variables, all static variables, and anything you allocate yourself are in XMM RAM. But local non-static variables are allocated on the stack - which is always in Hub RAM. This means they can be allocated and vanish automatically and at zero cost when the block is entered or exits. Managing the allocation and deallocation of local variables in XMM RAM would be painfully slow. If you really want variables to be in XMM RAM, you can either declare them as globals, declare them as locals but make them static, or dynamically allocate them.

    Ross.

    The thing is, if I, say, have a PASM driver that needs some buffer to work in, where do I get that from?

    In PASM, the simplest way is to allocate it during startup using the FREE_MEM pointer, which tells Catalina what Hub RAM plugins need for themselves, and hence what memory is left for Catalina to use. See target\Catalina_plugins.inc for some examples.

    Once you are executing C, you can allocate the space on your local stack and then pass a suitable pointer to wherever it is needed. For an example see demos\multithread\test_dynamic_kernel.c which allocate stack space in Hub RAM and then pass it to the new kernel cog to use.

    Ross.

  • RossHRossH Posts: 5,477
    edited 2023-06-08 00:57

    An update:

    Some improvements in the time required to compile C programs, thanks in part to @Wuerfel_21

    Here are the current compile times on my P2 EDGE:

    hello_world.c (5 lines) - 10 minutes (was 15)
    othello.c (470 lines) - 13 minutes (was 20)
    startrek.c (2200 lines) - 56 minutes (was 105!)
    chimaera.c (5500 lines) - 170 minutes

    Another important piece of news is that while looking at where the slowdowns were happening, I found a bug in my SD card plugin that has allowed me to both fix an occasional random error, and also speed things up a bit. I am not ready to release Catalina 6.0 yet, but I will release Catalina 5.9.2 soon with this fix and also Wuerfel_21's improvements to the PASM assembler.

    On the downside, Microsoft recently released an update for Windows 10 which has broken a few things - not just Catalina, but also some MinGW utilities. They do this occasionally, and it is really annoying that you can't "undo" updates any more :( I will have to spend some time figuring out a workaround until they fix it - if they ever do. They still haven't fixed the last breakage they inflicted on us in the command line interpreter. I suspect they do this deliberately every so often just to force people to upgrade. They did the same thing to Windows 7.

    Ross.

    EDIT: added times for chimaera.c

  • evanhevanh Posts: 16,027

    @RossH said:
    On the downside, Microsoft recently released an update for Windows 10 which has broken a few things - not just Catalina, but also some MinGW utilities. They do this occasionally, and it is really annoying that you can't "undo" updates any more :( I will have to spend some time figuring out a workaround until they fix it - if they ever do. They still haven't fixed the last breakage they inflicted on us in the command line interpreter. I suspect they do this deliberately every so often just to force people to upgrade. They did the same thing to Windows 7.

    Would certainly be true to form for M$. They've even reverted to locking the install of the web browser again. I'm surprised they think that's okay after the fines they incurred for that last time. I guess they know it's possible to get away with again.

  • RossHRossH Posts: 5,477
    edited 2023-06-16 07:43

    An update:

    I have added a BETA release of Catalina 6.0 here. It contains zip files compiled for a P2_EDGE with PSRAM (i.e. a P2-EC32MB) and a P2_EVAL with the HyperRAM add-on board.

    Here is an extract from the README.TXT:

    New Functionality
    -----------------
    
    1. There is a new Catalyst demo - Catalina itself! The demos\catalyst folder
       has a new subdirectory called 'catalina', in which a self-hosting version
       of Catalina can be built. Since it is supported only on a limited number
       of Propeller 2 platforms, this new demo is not built by default when you
       build Catalyst. If you have a P2 EDGE board with PSRAM or a P2 EVAL board
       with HyperRAM then you can build it manually - go to the 'catalina' 
       subdirectory, and use the build_all script with the same options you
       used to build Catalyst. For example
    
          cd catalina
          build_all P2_EDGE SIMPLE VT100 CR_ON_LF USE_COLOR OPTIMIZE MHZ_200
       or
          cd catalina
          build_all P2_EVAL VGA COLOR_4 OPTIMIZE MHZ_200
    
       Note that the build_all script will automatically use the copy_all script
       to copy the results to the Catalyst image folder.
    
       The self-hosted version of Catalina currently has the following limitations:
    
          - it supports the Propeller 2 only.
          - it supports the TINY, COMPACT and NATIVE modes only.
          - it does not support the Catalina debugger, optimizer or parallelizer.
    
       Since the self-hosting version of catalina introduces a 'bin' directory to 
       hold the catalina executables, the build_all and copy_all scripts now put 
       their output in a directory called 'image' instead of 'bin'. See the
       Catalyst Reference Manual for more details on the self-hosted version of 
       Catalina.
    

    I will repeat the main warning here for those who don't bother to read the whole README.TXT:

    The larger C demo programs can take a long time to compile. Even the standard C "hello world" program (hello.c) takes 10 minutes. Compiling the "chimaera" game (chimaera.c, approx 5,500 lines of C code) takes about 170 minutes.

    More speed improvements are expected over time.

    Ross.

    EDIT: Post updated to reflect that there is now a complete BETA release available, not just a preview.

  • RossHRossH Posts: 5,477

    @RossH said:
    I will release Catalina 6.0 as soon as I can resolve one remaining issue - which I believe may actually be a Windows bug. As you can see from the demo (which was itself compiled using Catalina 6.0) I have a workaround for the problem, but I am not willing to release it until I understand what is going on.

    Ah! Found it! :(

    Something has changed about how Windows manages the user's TEMP folder, which by default is %USERPROFILE%\AppData\Local\Temp.

    Catalina has always assumed that if it creates a file in that folder, it will remain there for the duration of the compilation process. But this no longer seems to be guaranteed. Files seem to vanish from that folder sometime after the process that created them exits. This is not the expected behaviour, and it seems to be particular to that folder - setting the user's TEMP environment variable to any other folder seems to restore the expected behaviour, which is that files created in the TEMP folder persist until they are explicitly deleted. Not sure when this change took place, but Catalina 6.0 changes the way the various compilation sub-processes are invoked, which means it is now affected by this change.

    Not sure of the best way to fix this yet, but at least I now understand what is going on. Micro$oft strikes again.

    Ross.

  • evanhevanh Posts: 16,027

    Lol, you'd think some automatic cleaner would wait at least a day or two before vacuuming up random temp files. Can't be by design, surely.

  • ke4pjwke4pjw Posts: 1,169
    edited 2023-06-08 14:53

    @RossH Windows doesn't do that. You have installed something doing that or your AV is doing it. Use Process Explorer to figure out what is doing it.

  • RossHRossH Posts: 5,477

    @ke4pjw said:
    @RossH Windows doesn't do that. You have installed something doing that or your AV is doing it. Use Process Explorer to figure out what is doing it.

    You may be right. The problem is back again this morning. Anti-virus scans show nothing. System File Check shows nothing. The odd part is that executing the commands on the command line always works. But when exactly the same commands are executed via the the GnuWin 'make' utility they nearly always fail (again!). And any temporary files created by the commands all disappear.

    Process Explorer did give me one clue - when the command is executed via 'make' one of the sub-processes gets an access violation. But when the same command is executed via the command line it does not. This is why I think it may be something to do with the way sub-processes are being executed by 'make', but I have been using the same version of 'make' for years and never had a problem with it, which is why I am thinking it is a change in Windows, which seems to be constantly updating things :(.

    Luckily, my workaround still works. Which is simply to add a few more options to any command to be executed by 'make' - it does not depend on which options are added, but the more options the more reliable it seems to be. But I don't understand why this works and it is not 100% reliable, so it is back to the drawing board. :(

    One possibility is that it is simply to do with timing, and slowing down the execution of each command (which adding more options would do, albeit only by a tiny amount) may be just enough to make it work. I will try that next.

    Ross.

  • Try disabling your AV and/or Windows Defender and see if that makes a difference. So many AVs will silently kill processes they think are a threat.

Sign In or Register to comment.