Come to think of it, I think it best just to rely on malloc as-is. Allow over-commit to be handled (slowly) by the OS.
If the program causes the system to slow down because of over-commit, that's their problem. If malloc is returning a pointer you can't use, that's an OS or library problem. I'd create a test program to be sure and submit it as a bug. Thanks. Blake On Tue, Dec 28, 2021 at 1:05 PM Blake McBride <blake1...@gmail.com> wrote: > Hi Jürgen, > > First, to be sure you know where I'm coming from. I know malloc() and > sbrk() pretty well. I've written malloc's before. > > It is utter news to me that malloc would return a pointer that you can't > use - even in over-commit situations. Theoretically, in over-commit > situations, if you access a pointer that's paged out, the system should > page it in first. If that doesn't happen, that's a bug in the OS. I'd > create a simple program that mimics this behavior and submit it as a bug > report. (What I almost always find is that during the process of creating > the sample program, I find the actual bug in my own program.) > > What I'd try to do is write my own malloc that does the following: > > 1. Checks for the amount of real memory available using something I took > from the *free* command. > > 2. If there is enough memory, call the real malloc and return it. > > 3. If there is not enough real memory, rather than calling malloc and > causing paging you can just return a null. > > Of course, I understand that sbrk() adjusts the system breakpoint > essentially giving you more heap space. Malloc manages that free space > that's already been taken from the OS via sbrk(). > > With regard to item #1, you'd have to have an intelligent way of > determining when to calculate the true available space. You can't do it > for 16 bytes, and you don't want to do it for every allocation. On the > other hand for large allocations or if you haven't queried the system for > some time, it makes sense to update your opinion of the available space. > > Thanks. > > Blake > > > > > On Tue, Dec 28, 2021 at 12:50 PM Dr. Jürgen Sauermann < > mail@jürgen-sauermann.de> wrote: > >> Hi Blake, >> >> I know. The problem is that other processes may, just as GNU APL does, >> allocate memory >> successfully via malloc(). malloc, on "success" returns a non-zero >> virtual memory address. >> >> The fact that malloc returns a non-zero address (= indicating malloc() >> success) does unfortunately >> not guarantee that you would be able to access that memory. You may or may >> not get a page fault when accessing the memory. The point in time when >> you call 'free' or >> similar tools (which basically read /proc/memory tell you the situation >> at that time which can >> be very different from the situation when you fill your virtual memory >> with real data (which >> assigns a physical memory page to the successfully allocated virtual >> memory page) and >> that may fail. Even worse, when this happen then your machine has no more >> memory and >> the C++ exception handlers will also fails due to lack of memory and the >> process is terminated >> without any chance to raise a WS full or even remain in apl. >> >> Best Regards, >> Jürgen >> >> >> On 12/28/21 6:52 PM, Blake McBride wrote: >> >> Since the Linux "free" command knows the difference between how much real >> memory vs. virtual memory is available, it may be useful to use a similar >> method. >> >> Blake >> >> >> On Tue, Dec 28, 2021 at 11:34 AM Dr. Jürgen Sauermann < >> mail@jürgen-sauermann.de> wrote: >> >>> Hi Russ, >>> >>> it has turned out to be very difficult to find a reliable way to figure >>> the memory that is available for >>> a process like the GNU APL interpreter. One (of several) reasons for >>> that is that GNU linux tells you >>> that it has more memory than it really has (so-called over-commitment). >>> You can malloc() more memory >>> than you have and malloc will return a virtual memory (and no error) >>> when you ask for it. However, if you >>> access the virtual memory at a later point in time (i.e. requesting real >>> memory for it) then the process >>> may crash badly if that real memory is not available. >>> >>> GNU APL deals with this by assuming (by default) that the total memory >>> available for the GNU APL process is about 2 GB, >>> >>> You can increase that amount (outside apl) be setting a larger memory >>> limit, probably with: >>> >>> *ulimit --virtual-memory-size* or *ulimit -v *(depending on platform). >>> >>> in the shell or script before starting apl. However, note that: >>> >>> * *ulimit -v* *ulimited* will not work because this is the default and >>> GNU APL will apply its default of 2 GB in this case, >>> * the WS FULL behaviour of GNU APL (ans ⎕WA) will become unreliable. You >>> must ensure that GNU APL will get >>> as much memory as you promised it with ulimit, and >>> * all GNU APL cells have the same size (e.g. 24 byte on a 64-bit CPU, >>> see *apl -l37*) even if the cells contain only >>> Booleans. >>> >>> The workaround for really large Boolean arrays is to pack them into the >>> 64-bits of a GNU APL integer >>> and maybe use the bitwise functions (⊤∧, ⊤∨, ...) of GNU APL to access >>> them group-wise. >>> >>> Best Regards, >>> Jürgen >>> >>> >>> On 12/28/21 3:53 AM, Russtopia wrote: >>> >>> Hi, doing some experiments in learning APL I was writing a word >>> frequency count program that takes in a document, identifies unique words >>> and then outputs the top 'N' occurring words. >>> >>> The most straightforward solution, to me, seems to be ∘.≡ which works up >>> to a certain dataset size. The main limiting statement in my program is >>> >>> wordcounts←+⌿ (wl ∘.≡ uniqwords) >>> >>> .. which generates a large boolean array which is then tallied up for >>> each unique word. >>> >>> I seem to run into a limit in GNU APL. I do not see an obvious ⎕SYL >>> parameter to increase the limit and could not find any obvious reference in >>> the docs either. What are the absolute max rows/columns of a matrix, and >>> can the limit be increased? Are they separate or a combined limit? >>> >>> 5 wcOuterProd 'corpus/135-0-5000.txt' ⍝⍝ 5000-line document >>> Time: 26419 ms >>> the of a and to >>> 2646 1348 978 879 858 >>> ⍴wl >>> 36564 >>> ⍴ uniqwords >>> 5695 >>> >>> 5 wcOuterProd 'corpus/135-0-7500.txt' ⍝⍝ 7500-line document >>> WS FULL+ >>> wcOuterProd[8] wordcounts←+⌿(wl∘.≡uniqwords) >>> ^ ^ >>> ⍴ wl >>> 58666 >>> ⍴ uniqwords >>> 7711 >>> >>> >>> I have an iterative solution which doesn't use a boolean matrix to count >>> the words, rather looping through using pick/take and so can handle much >>> larger documents, but it takes roughy 2x the execution time. >>> >>> Relating to this, does GNU APL optimize boolean arrays to minimize >>> storage (ie., using larger bit vectors rather than entire ints per bool) >>> and is there any clever technique other experience APLers could suggest to >>> maintain the elegant 'loop-free' style of computing but avoid generating >>> such large bool matrices? I thought of perhaps a hybrid approach where I >>> iterate through portions of the data and do partial ∘.≡ passes but of >>> course that complicates the algorithm. >>> >>> [my 'outer product' and 'iterative' versions of the code are below] >>> >>> Thanks, >>> -Russ >>> >>> --- >>> #!/usr/local/bin/apl --script >>> ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ >>> ⍝ ⍝ >>> ⍝ wordcount.apl 2021-12-26 20:07:07 (GMT-8) ⍝ >>> ⍝ ⍝ >>> ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ >>> >>> ⍝ function edif has ufun1 pointer 0! >>> >>> ∇r ← timeMs; t >>> t ← ⎕TS >>> r ← (((t[3]×86400)+(t[4]×3600)+(t[5]×60)+(t[6]))×1000)+t[7] >>> ∇ >>> >>> ∇r ← lowerAndStrip s;stripped;mixedCase >>> stripped ← ' >>> abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz*' >>> mixedCase ← ⎕av[11],' >>> ,.?!;:"''()[]-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' >>> r ← stripped[mixedCase ⍳ s] >>> ∇ >>> >>> ∇c ← h wcIterative fname >>> ⍝⍝;D;WL;idx;len;word;wc;wcl;idx >>> ⍝⍝ Return ⍒-sorted count of unique words in string vector D, ignoring >>> case and punctuation >>> ⍝⍝ @param h(⍺) - how many top word counts to return >>> ⍝⍝ @param D(⍵) - vector of words >>> ⍝⍝⍝⍝ >>> D ← lowerAndStrip (⎕fio['read_file'] fname) ⍝ raw text with newlines >>> timeStart ← timeMs >>> D ← (~ D ∊ ' ') ⊂ D ⍝ make into a vector of words >>> WL ← ∪D >>> ⍝⍝#DEBUG# ⎕ ← 'unique words:',WL >>> wcl ← 0⍴0 >>> idx ← 1 >>> len ← ⍴WL >>> count: >>> ⍝⍝#DEBUG# ⎕ ← idx >>> →(idx>len)/done >>> word ← ⊂idx⊃WL >>> ⍝⍝#DEBUG# ⎕ ← word >>> wc ← +/(word≡¨D) >>> wcl ← wcl,wc >>> ⍝⍝#DEBUG# ⎕ ← wcl >>> idx ← 1+idx >>> → count >>> done: >>> c ← h↑[2] (WL)[⍒wcl],[0.5]wcl[⍒wcl] >>> timeEnd ← timeMs >>> ⎕ ← 'Time:',(timeEnd-timeStart),'ms' >>> ∇ >>> >>> ∇r ← n wcOuterProd fname >>> ⍝⍝ ;D;wl;uniqwords;wordcounts;sortOrder >>> D ← lowerAndStrip (⎕fio['read_file'] fname) ⍝ raw text with newlines >>> timeStart ← timeMs >>> wl ← (~ D ∊ ' ') ⊂ D >>> ⍝⍝#DEBUG# ⎕ ← '⍴ wl:', ⍴ wl >>> uniqwords ← ∪wl >>> ⍝⍝#DEBUG# ⎕ ← '⍴ uniqwords:', ⍴ uniqwords >>> wordcounts ← +⌿(wl ∘.≡ uniqwords) >>> sortOrder ← ⍒wordcounts >>> r ← n↑[2] uniqwords[sortOrder],[0.5]wordcounts[sortOrder] >>> timeEnd ← timeMs >>> ⎕ ← 'Time:',(timeEnd-timeStart),'ms' >>> ∇ >>> >>> >>> >>> >>