awk中的关联数组具有挑战性的内存限制

时间:2014-09-29 22:08:37

标签: memory awk gawk

这与我最近在Awk code with associative arrays -- array doesn't seem populated, but no erroroptimizing loop, passing parameters from external file, naming array arguments within awk

中的帖子有关

我的基本问题只是从详细的古代档案金融市场数据,#transactions,#shares,value,BY DATE,FIRM-ID,EXCHANGE等的每日汇总进行计算。学习在awk中使用关联数组,并且很高兴能够在11分钟的时钟时间内处理9,900多万行。直到我喝完咖啡之前。

变得更加雄心勃勃,从2个阵列下标转移到4个,现在我无法一次处理超过6500行。

获取表单的错误消息:

  

K:\用户文件夹\ KRISHNANM \ PAPERS \ FII_Transaction_Data> zcat   RAW_DATA \ 2003_1.zip | gawk -f CODE \ FII_daily_aggregates_v2.awk>   OUTPUT \ 2003_1.txt&安培;

     

gawk:CODE \ FII_daily_aggregates_v2.awk:33:(FILENAME = - FNR = 49300)   致命:more_no des:nextfree:无法分配内存(空间不足)

在一些运行中,机器告诉我它缺少52 KB的内存。我对Win-7和8MB RAM的std配置有所了解。

(经济学家通过培训,而不是计算机科学家。)我意识到从2到4阵列使计算机上的问题在计算上变得更加复杂,但是有一些人可以做些什么来改善内存管理至少一点点。我试过关闭我正在做的其他事情。该错误始终只能用于内存,绝不能用于磁盘空间或其他任何内容。

示例INPUT:

49290,C198962542782200306,6/30/2003,433581,F5811773991200306,S5405611832200306,B5086397478200306,NESTLE INDIA LTD.,INE239A01016,6/27/2003,1,E9035083824200306,REG_DL_STLD_02,591.13,5655,3342840.15,REG_DL_INSTR_EQ,REG_DL_DLAY_P,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00
49291,C198962542782200306,6/30/2003,433563,F6292896459200306,S6344227311200306,B6110521493200306,GRASIM INDUSTRIES LTD.,INE047A01013,6/27/2003,1,E9035083824200306,REG_DL_STLD_02,495.33,3700,1832721,REG_DL_INSTR_EQ,REG_DL_DLAY_P,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00
49292,C198962542782200306,6/30/2003,433681,F6513202607200306,S1724027402200306,B6372023178200306,HDFC BANK LTD,INE040A01018,6/26/2003,1,E745964372424200306,REG_DL_STLD_02,242,2600,629200,REG_DL_INSTR_EQ,REG_DL_DLAY_D,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00
49293,C7885768925200306,6/30/2003,48128,F4406661052200306,S7376401565200306,B4576522576200306,Maruti Udyog Limited,INE585B01010,6/28/2003,3,E912851176274200306,REG_DL_STLD_04,125,44600,5575000,REG_DL_INSTR_EQ,REG_DL_DLAY_P,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00
49294,C7885768925200306,6/30/2003,48129,F4500260787200306,S1312094035200306,B4576522576200306,Maruti Udyog Limited,INE585B01010,6/28/2003,4,E912851176274200306,REG_DL_STLD_04,125,445600,55700000,REG_DL_INSTR_EQ,REG_DL_DLAY_P,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00
49295,C7885768925200306,6/30/2003,48130,F6425024637200306,S2872499118200306,B4576522576200306,Maruti Udyog Limited,INE585B01010,6/28/2003,3,E912851176274200306,REG_DL_STLD_04,125,48000,6000000,REG_DL_INSTR_EU,REG_DL_DLAY_P,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00

代码

BEGIN { FS = "," } 
# For each array subscript variable -- DATE ($10), firm_ISIN ($9), EXCHANGE ($12), and FII_ID ($5), after checking for type = EQ, set up counts for each value, and number of unique values.
         ( $17~/_EQ\>/ )    {    if (date[$10]++ == 0) date_list[d++] = $10;
                                 if (isin[$9]++ == 0) isin_list[i++] = $9;
                                 if (exch[$12]++ == 0) exch_list[e++] = $12;
                                 if (fii[$5]++ == 0) fii_list[f++] = $5;
                                 }
# For cash-in, buy (B), or cash-out, sell (S) count NR = no of records, SH = no of shares, RV = rupee-value.
         (( $17~/_EQ\>/ ) && ( $11~/1|2|3|5|9|1[24]/ )) {{ ++BNR[$10,$9,$12,$5]} {BSH[$10,$9,$12,$5] += $15} {BRV[$10,$9,$12,$5] += $16} }
         (( $17~/_EQ\>/ ) && ( $11~/4|1[13]/ ))         {{ ++SNR[$10,$9,$12,$5]} {SSH[$10,$9,$12,$5] += $15} {SRV[$10,$9,$12,$5] += $16} }
 END  { 
          { print NR, "records processed."}
          { print "  " }
          { printf("%-11s\t%-13s\t%-20s\t%-19s\t%-7s\t%-7s\t%-14s\t%-14s\t%-18s\t%-18s\n",    \
           "DATE", "ISIN", "EXCH", "FII", "BNR", "SNR", "BSH", "SSH", "BRV", "SRV")  }
       { for (u = 0; u < d; u++)  
             {
              for (v = 0; v < i; v++)
                  {
                   for (w = 0; w < e; w++)
                       {
                        for (x = 0; x < f; x++)
                        #check first below for records with zeroes, don't print them
                            {    if  (BNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]] + SNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]] > 0)
                                      { BR = BNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
                                        SR = SNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
                                        BS = BSH[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
                                        BV = BRV[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
                                        SS = SSH[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
                                        SV = SRV[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
                                       { printf("%-11s\t%13s\t%20s\t%19s\t%7d\t%7d\t%14d\t%14d\t%18.2f\t%18.2f\n",    \
                                              date_list[u], isin_list[v], exch_list[w], fii_list[x], BR, SR, BS, SS, BV, SV) } }
                            }
                        }
                   }
               }
         }
  }

预期输出

 6 records processed.

DATE        ISIN            EXCH                    FII                 BNR     SNR     BSH             SSH             BRV                 SRV               
6/27/2003    INE239A01016      E9035083824200306      F5811773991200306       1       0           5655               0          3342840.15                0.00
6/27/2003    INE047A01013      E9035083824200306      F6292896459200306       1       0           3700               0          1832721.00                0.00
6/26/2003    INE040A01018    E745964372424200306      F6513202607200306       1       0           2600               0           629200.00                0.00
6/28/2003    INE585B01010    E912851176274200306      F4406661052200306       1       0          44600               0          5575000.00                0.00
6/28/2003    INE585B01010    E912851176274200306      F4500260787200306       0       1              0          445600                0.00         55700000.00

在这种情况下,当输入记录的数量超过6500时,我最终会遇到内存问题。总共有大约700万条记录。

对于2数组下标问题,尽管在同一台机器上使用相同的GNU-AWK在11分钟的时钟时间内处理了12,900多万行的数据集,但请参阅optimizing loop, passing parameters from external file, naming array arguments within awk

问题:awk在内存管理方面不是很聪明,但是其他一些更现代的工具(比如说SQL)会用相同的内存资源完成这项任务吗?或者这只是关联数组的一个特征,我发现它让我能够避免对数据,许多循环和SORT程序进行多次传递,但是它可能很好地工作到2个数组下标,然后面对指数内存资源成本是什么?

后记:超级详细的几乎防止白痴的教程以及Ed Morton在下面的评论中提供的代码产生了巨大的差异,尤其是他的GAWK脚本tst.awk。他告诉我(a)智能地使用SUBSEP(b)处理不必要的循环,这在这个问题中至关重要,这个问题往往具有非常稀疏的数组,具有各种AWK结构。与我的旧代码的性能相比(在一台机器上只接受了多达6500行输入,另一台机器甚至无法达到这一点),从表中可以看出Ed Morton的tst.awk的性能下面:

**filename  start      end       min        in ln   out lines
2008_1  12:08:40 AM 12:27:18 AM 0:18        391438  301160
2008_2  12:27:18 AM 12:52:04 AM 0:24        402016  314177
2009_1  12:52:05 AM 1:05:15 AM  0:13        302081  238204
2009_2  1:05:15 AM  1:22:15 AM  0:17        360072  276768
2010_1  "slept"                         507496  397533
2010_2  3:10:26 AM  3:10:50 AM  0:00         76200   58228
2010_3  3:10:50 AM  3:11:18 AM  0:00         80988   61725
2010_4  3:11:18 AM  3:11:47 AM  0:00         86923   65885
2010_5  3:11:47 AM  3:12:15 AM  0:00         80670   63059**

只是在执行tst.awk之前和之后的行上使用%time%获得时间,所有这些都放在一个简单的批处理脚本中,&#34; min&#34;是所花费的时钟时间(默认情况下,EXCEL执行任何舍入),&#34;在ln&#34;和&#34;出线&#34;分别是输入和输出的行。 从处理我们拥有的整个数据,从2003年1月到2014年1月,我们发现理论最大输出记录数= #date * #ISINs * #Exchanges * #FIIs = 2992 * 2955 * 567 * 82268,而总输出线的实际数量仅为5,261,942,仅为理论最大值的1.275 * 10 ^( - 8) - 非常稀疏确实。虽然存在稀疏性,但我们之前确实猜过,但是数组可能非常稀疏 - 这对于内存管理来说非常重要 - 我们无法告知实际完成的事情,对于真实的数据集。所花费的时间似乎在输入大小中呈指数级增长,但在不会造成实际困难的限度内。非常感谢,Ed。

1 个答案:

答案 0 :(得分:11)

一般来说,关联数组没有问题。在awk中(除了用于真正的2D数组的gawk),具有4个下标的关联数组与具有2个下标的相关数组相同,因为实际上它只有一个下标,其是由SUBSEP分隔的每个伪下标的串联。

鉴于你说I am unable to process more than 6500 lines at a time.,问题远比你编写代码的方式更容易出现问题而不是任何基本的awk问题,所以如果你想要更多的帮助,请发布一个带有示例输入和预期输出的小脚本这将演示您的问题和尝试的解决方案,以了解我们是否有关于改善其内存使用情况的建议。

鉴于您发布的脚本,我预计问题出在你的END部分中的那些嵌套循环当你这样做:

for (i=1; i<=maxI; i++) {
    for (j=1; j<=maxJ; j++) {
        if  ( arr[i,j] != 0 ) {
            print arr[i,j]
        }
    }
}

你正在通过测试arr[i,j] != 0为循环之前不存在的i和j的每个可能组合创建arr [i,j]。如果您改为写道:

for (i=1; i<=maxI; i++) {
    for (j=1; j<=maxJ; j++) {
        if  ( (i,j) in arr ) {
            print arr[i,j]
        }
    }
}

然后循环本身不会在arr[]中创建新条目。

所以改变这个块:

if (BNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]] + SNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]] > 0)
{
    BR = BNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
    SR = SNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
    BS = BSH[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
    BV = BRV[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
    SS = SSH[date_list[u],isin_list[v],exch_list[w],fii_list[x]]
    SV = SRV[date_list[u],isin_list[v],exch_list[w],fii_list[x]]

可能不必要地将BNR,SNR,BSH,BRV,SSH和SRV中的每一个变成巨大但高度稀疏的数组,如下所示:

idx = date_list[u] SUBSEP isin_list[v] SUBSEP exch_list[w] SUBSEP fii_list[x]
BR = (idx in BNR ? BNR[idx] : 0)
SR = (idx in SNR ? SNR[idx] : 0)
if ( (BR + SR) > 0 )
{
    BS = (idx in BSH ? BSH[idx] : 0)
    BV = (idx in BRV ? BRV[idx] : 0)
    SS = (idx in SSH ? SSH[idx] : 0)
    SV = (idx in SRV ? SRV[idx] : 0)

如果有帮助,请告诉我们。另外,请检查您可能正在执行相同操作的其他地方的代码。

当你没有2时,你有4个下标出现这个问题的原因只是你在循环中有4个嵌套级别,现在你创建了更大,更稀疏的数组,当你只有2时。

最后 - 你的脚本中有一些奇怪的语法,其中一些@MarkSetchell在评论中指出,你的脚本效率不高,因为你没有使用else语句和所以测试多个条件并不可能都是真的,并且你反复测试相同的条件,并且它不稳健,因为你没有锚定你的RE(例如你测试/4|1[13]/而不是{{1}例如,您的/^(4|1[13])$/会匹配414等,而不仅仅是41,所以请将整个脚本更改为:

4

我在4个数组名称前面添加了$ cat tst.awk BEGIN { FS = "," } # For each array subscript variable -- DATE ($10), firm_ISIN ($9), EXCHANGE ($12), and FII_ID ($5), after checking for type = EQ, set up counts for each value, and number of unique values. $17 ~ /_EQ\>/ { if (!seenDate[$10]++) date_list[++d] = $10 if (!seenIsin[$9]++) isin_list[++i] = $9 if (!seenExch[$12]++) exch_list[++e] = $12 if (!seenFii[$5]++) fii_list[++f] = $5 # For cash-in, buy (B), or cash-out, sell (S) count NR = no of records, SH = no of shares, RV = rupee-value. idx = $10 SUBSEP $9 SUBSEP $12 SUBSEP $5 if ( $11 ~ /^([12359]|1[24])$/ ) { ++BNR[idx]; BSH[idx] += $15; BRV[idx] += $16 } else if ( $11 ~ /^(4|1[13])$/ ) { ++SNR[idx]; SSH[idx] += $15; SRV[idx] += $16 } } END { print NR, "records processed." print " " printf "%-11s\t%-13s\t%-20s\t%-19s\t%-7s\t%-7s\t%-14s\t%-14s\t%-18s\t%-18s\n", "DATE", "ISIN", "EXCH", "FII", "BNR", "SNR", "BSH", "SSH", "BRV", "SRV" for (u = 1; u <= d; u++) { for (v = 1; v <= i; v++) { for (w = 1; w <= e; w++) { for (x = 1; x <= f; x++) { #check first below for records with zeroes, don't print them idx = date_list[u] SUBSEP isin_list[v] SUBSEP exch_list[w] SUBSEP fii_list[x] BR = (idx in BNR ? BNR[idx] : 0) SR = (idx in SNR ? SNR[idx] : 0) if ( (BR + SR) > 0 ) { BS = (idx in BSH ? BSH[idx] : 0) BV = (idx in BRV ? BRV[idx] : 0) SS = (idx in SSH ? SSH[idx] : 0) SV = (idx in SRV ? SRV[idx] : 0) printf "%-11s\t%13s\t%20s\t%19s\t%7d\t%7d\t%14d\t%14d\t%18.2f\t%18.2f\n", date_list[u], isin_list[v], exch_list[w], fii_list[x], BR, SR, BS, SS, BV, SV } } } } } } ,因为通过约定数组测试值的预先存在通常被命名为seen。此外,当填充SNR [] etc数组时,我首先创建了一个idx变量,而不是每次重复使用字段数,以便将来更改它,并且主要是因为字符串连接在awk中相对较慢而且当你使用时会发生什么数组中的多个索引最好只显式地执行字符串连接。并且我将date_list [] etc数组更改为从1开始而不是零,因为所有awk生成的数组,字符串和字段编号从1开始。您可以手动创建一个从0或-357开始的数组或者您想要的任何数字但是如果你总是在1点开始拍摄,那么它有一天会让你自己在脚下拍摄。

我希望通过将嵌套循环限制为可能存在于封闭循环索引组合的值(例如,并非u + v + w的每个值都是可能的,因此有时候你应该这样做在x上打扰循环。例如:

seen