You are right that doing array allocations in any language is slow, and in
order to get some kind of baseline to compare things to, I implemented the
same algorithm in Lisp, and I did so in the worst possible way: By
completely reallocating the array for each row, and copying the old content
into the new one (code in the end of this message). I did it that way
because that is what the APL code ends up doing, so I didn't want to give
the Lisp program any benefits.This was, as you may imagine, very slow. It
took *51* seconds to load the entire file.

The APL version that I posted in my previous message takes

Now, for a solution. Reading the file twice is quite ugly, and I'd rather
avoid doing so.

I could rewrite the Lisp program to in-place reallocation of the array,
which would speed up the program by several orders of magnitude. The
problem is that there is no mechanism in GNU APL to do the same. I think
adding a new construct to the language to do that would be ugly, but
perhaps there is a way for GNU APL to detect the specific “append to the
end of an array” idiom so that it could use an optimised path that doesn't
require constructing a new array. *Jürgen*, is that even feasible?

Now, reading a CSV file like this is a very common case, and I am of the
opinion that having a dedicated native function for this purpose would
solve perhaps 75% of use cases where this is an issue. I'll be happy to
implement a flexible function that can do this, which would make GNU APL
much more pleasant to use for data analysis.

Jürgen, if I were to implement this, would you prefer it to be a
dynamically loaded library, or would you prefer having it as a
quad-function?

Regards,
Elias

On 18 January 2017 at 02:33, Nick Lobachevsky <ibeam2...@gmail.com> wrote:

> Elias,
>
> In just about any language, iterative string catenation is a real
> performance killer owing to the repeated growing memory allocation
> which just gets bigger with every iteration.
>
> I would restructure the algorithm to work more like this:  (sorry, on
> a Windoze box and no APL, so metacode only)
>
> Open the file, read the contents of the entire file, close the file
> Change NL to separator, then partition the whole thing (Z)
> Check to see that the length of the partitioned string divides evenly
> by pattern length
> Create a temporary of only the numeric items.  As they should all be
> scalar, you should be able to do it in a single execute and get a
> numeric vector of the correct length.
> Assign the numeric vector back into Z.  You may want to do a ravel
> each if you would rather have one element vectors
> Reshape Z to have the same number columns as in pattern
>
> Dyalog and some other APLs (APLX?) have a []CSV function.  The problem
> gets a bit harder when you have optional quotes around strings,
> embedded separators, and have to figure out yourself whether something
> is numeric or char.
>
>
>
> On 1/17/17, Elias Mårtenson <loke...@gmail.com> wrote:
> > I wanted to use GNU APL to work on a dataset of star data. The file
> > consists of 34030 lines of the following form:
> >
> >   892376 3813 4.47 0.4699  1.532  0.007    7306.69 0.823 0.4503 0 ---
> >  1026146 4261 4.57 0.6472 14.891  0.12    11742.56 1.405 0.7229 0 ---
> >  1026474 4122 4.56 0.5914  1.569  0.006   30471.8  1.204 0.6061 0 ---
> >  1162635 3760 4.77 0.4497 15.678  0.019   10207.47 0.978 0.5445 1 ---
> >
> > I wrote a generic CSV loader to handle this (source code at the end of
> this
> > email), and loaded the data like so:
> >
> > *    z ← 'nnnnnnnnnns' read_csv 'apjs492452t1_mrt.txt'*
> >
> > This took many minutes to load, which in my opinion shouldn't happen.
> >
> > Now, I have a few questions:
> >
> >
> >    1. Is there a way to speed up this code?
> >    2. Is there something that could be done on the GNU APL implementation
> >    side to make this faster?
> >    3. Shouldn't we have a generic ⎕CSV function or something like that
> >    which would be able to load CSV files in milliseconds regardless of
> > size?
> >    This should be trivial to do in C++.
> >
> > Here's the code in question:
> >
> > ∇Z ← type convert_entry value
> >   →('n'≡type)/numeric
> >   →('s'≡type)/string
> >   ⎕ES 'Illegal conversion type'
> > numeric:
> >   Z←⍎value
> >   →end
> > string:
> >   Z←value
> > end:
> > ∇
> >
> > ∇Z ← pattern read_csv filename ;fd;line;separator
> >   separator ← ' '
> >   Z ← 0 (↑⍴pattern) ⍴ ⍬
> >   fd ← 'r' FIO∆fopen filename
> >
> > next:
> >   line ← FIO∆fgets fd           ⍝ Read one line from the file
> >   →(⍬≡line)/end
> >   →(10≠line[⍴line])/skip_nl     ⍝ If the line ends in a newline
> >   line ← line[⍳¯1+⍴line]        ⍝ Remove the newline
> > skip_nl:
> >   line ← ⎕UCS line
> >   Z ← Z⍪ pattern convert_entry¨ (line≠separator) ⊂ line
> >   →next
> > end:
> >
> >   FIO∆fclose fd
> > ∇
> >
>

Reply via email to