gpt4 book ai didi

memory - awk 中的关联数组挑战内存限制

转载 作者:行者123 更新时间:2023-12-04 17:09:25 27 4
gpt4 key购买 nike

这与我最近在 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 分钟的时钟时间内处理 129 多万行。在我喝完咖啡之前。

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

获取表单的错误信息:

K:\User Folders\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) fatal: more_no des: nextfree: can't allocate memory (Not enough space)



在某些运行中,机器告诉我它缺少 52 KB 的内存。我有我认为的带有 Win-7 和 8MB RAM 的标准配置。

(受过培训的经济学家,而不是计算机科学家。)我意识到从 2 到 4 个数组会使计算机的计算问题变得更加复杂,但是是否可以做些什么来至少改善一点内存管理。我已经尝试关闭我正在做的所有其他事情。错误总是只与内存有关,与磁盘空间或其他任何东西无关。

样本输入:
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 分钟的时钟时间内处理了 129+ 百万行,请参阅 optimizing loop, passing parameters from external file, naming array arguments within awk

问题:是不是 awk 在内存管理方面不是很聪明,但其他一些更现代的工具(比如 SQL)会用相同的内存资源完成这个任务吗?或者这仅仅是关联数组的一个特性,我发现它可以让我避免多次传递数据、多次循环和 SORT 过程,但它可能在多达 2 个数组下标的情况下运行良好,然后在之后面临指数级的内存资源成本那?

后记: super 详细的几乎防白痴的教程以及 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% 获得的,所有这些都放在一个简单的批处理脚本中,“min”是所用的时钟时间(根据 EXCEL 默认情况下所做的任何舍入),“in ln”和“输出线”分别是输入和输出线。 通过处理我们拥有的整个数据,从 2003 年 1 月到 2014 年 1 月,我们发现理论最大输出记录数 = #dates*#ISINs*#Exchanges*#FIIs = 2992*2955*567*82268,而实际总输出行数仅为 5,261,942,仅为理论最大值的 1.275*10^(-8) - 确实非常稀疏 。有稀疏性,我们之前确实猜到了,但是数组可能非常稀疏——这对内存管理很重要——对于真实的数据集,在某些事情实际完成之前我们无法判断。花费的时间似乎在输入大小上呈指数增长,但在不会造成实际困难的限度内。非常感谢,埃德。

最佳答案

关联数组一般没有问题。在 awk 中(除了真正二维数组的 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]
}
}
}

您正在为循环之前不存在的 i 和 j 的每个可能组合创建 arr[i,j] 仅通过测试 arr[i,j] != 0 .如果你改为写:
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语句等测试不可能全部为真的多个条件,并且您正在重复测试相同的条件,并且它不稳健,因为您没有 anchor 定您的 RE(例如,您测试 /4|1[13]/ 而不是 /^(4|1[13])$/ 所以例如,您的 4 将匹配 1441 等,而不仅仅是 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在 4 个数组名称前面只是因为按照惯例,数组测试值的预先存在性通常被命名为 seen .此外,在填充 SNR[] 等数组时,我首先创建了一个 idx 变量,而不是每次都重复使用字段编号,以便将来更改它,主要是因为字符串连接在 awk 中相对较慢,这就是您使用时发生的情况数组中有多个索引,因此最好只显式地进行一次字符串连接。我将你的 date_list[] 等数组更改为从 1 开始而不是从 0 开始,因为所有 awk 生成的数组、字符串和字段编号都从 1 开始。你可以手动创建一个从 0 或 -357 或任何你想要的数字开始的数组,但是如果你总是从 1 开始,那么总有一天你会避免用脚射击自己。

我希望通过将嵌套循环限制为仅对封闭循环索引组合可能存在的值(例如,并非 u+v+w 的每个值都是可能的,因此有时您不应该打扰),它可以变得更高效在 x 上循环)。例如:
$ 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])$/ ) {
seen[$10,$9]
seen[$10,$9,$12]
++BNR[idx]; BSH[idx] += $15; BRV[idx] += $16
}
else if ( $11 ~ /^(4|1[13])$/ ) {
seen[$10,$9]
seen[$10,$9,$12]
++SNR[idx]; SSH[idx] += $15; SRV[idx] += $16
}
}
END {
printf "d = %d\n", d | "cat>&2"
printf "i = %d\n", i | "cat>&2"
printf "e = %d\n", e | "cat>&2"
printf "f = %d\n", f | "cat>&2"

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++)
{
date = date_list[u]
for (v = 1; v <= i; v++)
{
isin = isin_list[v]
if ( (date,isin) in seen )
{
for (w = 1; w <= e; w++)
{
exch = exch_list[w]
if ( (date,isin,exch) in seen )
{
for (x = 1; x <= f; x++)
{
fii = fii_list[x]
#check first below for records with zeroes, don't print them
idx = date SUBSEP isin SUBSEP exch SUBSEP fii
if ( (idx in BNR) || (idx in SNR) )
{
if (idx in BNR)
{
bnr = BNR[idx]
bsh = BSH[idx]
brv = BRV[idx]
}
else
{
bnr = bsh = brv = 0
}

if (idx in SNR)
{
snr = SNR[idx]
ssh = SSH[idx]
srv = SRV[idx]
}
else
{
snr = ssh = srv = 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, isin, exch, fii, bnr, snr, bsh, ssh, brv, srv
}
}
}
}
}
}
}
}

关于memory - awk 中的关联数组挑战内存限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26109696/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com