I think what you're talking about and what I am talking about are not
exactly the same thing.  I can see what you are saying, but that is a
contrived example.  For example:

Level 1:  you are using the RAM that exists (not over-committed)

Level 2:  you are using more RAM than you have causing over-commit and
paging.

Level 3:  you allocate more memory than exists in RAM + paging.

I am talking about Levels 1 and 2.  You are talking about Level 3.  At
least in my world, I have never hit Level 3 because Level 2 becomes
intolerable slow and I kill the process myself.  For this reason, in my
world, Level 3 never occurs.

It doesn't make sense to handle Level 2 problems with Level 3 logic.

Blake


On Tue, Dec 28, 2021 at 1:16 PM Elias Mårtenson <loke...@gmail.com> wrote:

> Actually, the default memory allocator in glibc uses mmap and not sbrk, if
> I'm not mistaken. However, they both do the same thing at the end of the
> day, and it's to request more pages from the kernel.
>
> Unfortunately, Linux malloc never returns NULL. Even if you try to
> allocate a petabyte in one allocation. You can write to this memory and new
> pages will be created as you write, and at some point your write will fail
> with a SEGV because there are no free page left.
>
> What's even worse is that once you start to fail, the kernel will randomly
> kill processes until it gets more free pages. If that sounds idiotic,
> that's because it is, but that's how it works.
>
> You can configure the kernel to use not do this, but unfortunately you're
> going to have other problems if you do, primarily because all Linux
> software is written with the default behaviour in mind.
>
> You can try it right now. Write a C program that allocates a few TB of RAM
> and see what happens. Actually, nothing will happen until you start writing
> to this memory.
>
> Den ons 29 dec. 2021 03:09Blake McBride <blake1...@gmail.com> skrev:
>
>> 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'
>>>> ∇
>>>>
>>>>
>>>>
>>>>
>>>

Reply via email to