1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
|
\language=30
\input texinfo
@settitle Manuale utente di @pmg{}
@documentlanguage it
@c TODO: Checklist for release:
@c revise all U P D A T E items as appropriate
@c check all to-do notes
@c remove most comments
@c spell check (last 2am 15 Aug 2022)
@c verbatim limits: 47 rows x 75 cols, smallformat 58 x 90
@macro gwk {}
@command{gawk}
@end macro
@macro pmg {}
pm-@gwk{}
@end macro
@set TYTL Manuale Utente per la Memoria Persistente in @gwk{}
@setfilename pm-gawk.info
@settitle @value{TYTL}
@dircategory Manipolazione e creazione di testi
@direntry
* pm-gawk: (pm-gawk). Versione di gawk con la Memoria Persistente.
@end direntry
@fonttextsize 11
@c it seems to do no harm and possibly some good if color
@c distinguishes internal links from URLs to outside web
@tex
\gdef\linkcolor{0.12 0.09 .5} % TK's attempt at subdued blue
%\gdef\linkcolor{0.5 0.09 0.12} % Dark Red
\gdef\urlcolor{0.5 0.09 0.12} % Dark Red
\global\urefurlonlylinktrue
@end tex
@setchapternewpage off
@copying
@noindent
@c UPDATE copyright info below
Copyright @copyright{} 2022 Terence Kelly @*
@ifnottex
@noindent
@email{tpkelly@@eecs.umich.edu} @*
@email{tpkelly@@cs.princeton.edu} @*
@email{tpkelly@@acm.org} @*
@url{http://web.eecs.umich.edu/~tpkelly/pma/} @*
@url{https://dl.acm.org/profile/81100523747}
@end ifnottex
@noindent
A chiunque @`e permesso copiare, distribuire e/o modificare questo
documento, nei termini della Licenza ``GNU Free Documentation'',
Versione 1.3, o qualsiasi versione successiva, pubblicata dalla
Free Software Foundation, lasciando invariate le sezioni
``Introduzione'' e ``Storia'',
nessun testo in copertina o nell'ultima pagina di copertina.
Una copia della licenza è disponibile nel sito @*
@url{https://www.gnu.org/licenses/fdl-1.3.html}
@end copying
@titlepage
@title @value{TYTL}
@c UPDATE date below
@subtitle 16 agosto 2022
@subtitle @gwk{} versione 5.2
@subtitle @pmg{} versione 2022.08Aug.03.1659520468 (Avon 7)
@author Terence Kelly
@author @email{tpkelly@@eecs.umich.edu}
@author @email{tpkelly@@cs.princeton.edu}
@author @email{tpkelly@@acm.org}
@author @url{http://web.eecs.umich.edu/~tpkelly/pma/}
@author @url{https://dl.acm.org/profile/81100523747}
@vskip 0pt plus 1filll
@insertcopying
@end titlepage
@headings off
@c @contents @c no need for this in a short document
@node Top
@ifnottex
@ifnotxml
@ifnotdocbook
@top Introduzione generale
@gwk{} 5.2 introduce la funzionalità @emph{Memoria Persistente} he può
``ricordare'' le variabili e le funzioni definite in uno script che
possono quindi essere utilizzate in successive esecuzioni di script @gwk{};
ciò consente di passare variabili fra script non correlati fra loro
senza serializzare/analizzare file di testo; è inoltre possibile
gestire file più grossi della memoria disponibile e dei file di swap
[del computer]. Questo manuale supplementare fornisce una descrizione
approfondita della Memoria Persistente in @gwk{}.
@insertcopying
@end ifnotdocbook
@end ifnotxml
@end ifnottex
@menu
* Introduzione::
* Per iniziare::
* Esempi::
* Prestazioni::
* Integrità dei dati::
* Ringraziamenti::
* Installazione::
* Debugging::
* Storia::
@end menu
@c ==================================================================
@node Introduzione
@chapter Introduzione
@sp 1
@c UPDATE below after official release
GNU AWK (@gwk{}) 5.2, rilasciato nel mese di settembre 2022, introduce
la nuova funzionalità della @emph{memoria persistente} che facilita
la preparazione di script AWK e in certi casi ne migliora le prestazioni.
La nuova funzionalità, chiamata ``@pmg{}'',
può ``ricordare'' le variabili e le funzioni definite in uno script,
che possono quindi essere utilizzate in successive esecuzioni di script @gwk{}
e consente di passare variabili fra script non correlati fra loro
senza serializzare/analizzare file di testo---in maniera praticamente
trasparente. @pmg{} @emph{non} richiede presenza di memoria non-volatile
sul computer in cui viene eseguito, e neppure altre infrastrutture
insolite; funzione sui normali computer e sui normali sistemi operativi
che la maggior parte delle persone sta utilizzando da decenni.
@c @sp 1
@c TODO: ADR: hyperlinks to info page below
@noindent
La documentazione principale di @gwk{}@footnote{Vedere
@url{https://www.gnu.org/software/gawk/manual/},
@code{man gawk} @w{ } e @w{ } @code{info gawk}.} spiega il
funzionamento di base della nuova funzionalità di persistenza.
Questo manuale supplementare fornisce ulteriori dettagli,
esempi di programmi, e uno sguardo ``sotto il coperchio'' a
@pmg{}. Chi abbia familiarità con @gwk{} e con gli ambienti software
di tipo Unix, può proseguire direttamente leggendo: @*
@itemize @c @w{}
@item @ref{Per iniziare} basta scrivere solo qualche carattere in più.
@item @ref{Esempi} mostra come @pmg{} tratta tipici script AWK.
@item @ref{Prestazioni} riguarda efficienza, regolazione S.O. e altro.
@item @ref{Integrità dei dati} spiega come evitare di perdere i dati.
@item @ref{Ringraziamenti} a chi ha reso possibile @pmg{}.
@item @ref{Installazione} spiega dove ottenere @pmg{}.
@item @ref{Debugging} spiega come gestire eventuali errori.
@item @ref{Storia} traccia la tecnologia che sta dietro a @pmg{}.
@end itemize
@c UPDATE: revise above when content finalized
@c @sp 1
@noindent
Si può trovare la versione più recente di questo manuale, e anche
la ``versione del regista'', nel sito web dedicato all'allocatore
di memoria persistente usato in @pmg{}: @*
@center @url{http://web.eecs.umich.edu/~tpkelly/pma/}
@c @sp 1
La traduzione italiana più recente di questo manuale si può trovare
in: @*
@center @url{https://sites.google.com/view/gawkdoc-it/home-page}
@c @sp 1
@noindent
Due pubblicazioni descrivono [in inglese] l'allocatore di memoria
persistente, e le prime esperienze con un prototipo di
@pmg{} preparato a partire da una diramazione della distribuzione
sorgente ufficiale di @gwk{}:
@itemize
@item @url{https://queue.acm.org/detail.cfm?id=3534855}
@item @url{http://nvmw.ucsd.edu/nvmw2022-program/nvmw2022-data/@*nvmw2022-paper35-final_version_your_extended_abstract.pdf}
@end itemize
@c @sp 1
@noindent
Sentitevi liberi di inviarmi domande, suggerimenti, e resoconti di esperienze a: @*
@noindent
@w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @email{tpkelly@@eecs.umich.edu} @w{ } (preferito) @*
@w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @email{tpkelly@@cs.princeton.edu} @*
@w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @w{ } @email{tpkelly@@acm.org}
@c @page
@sp 3
@c ==================================================================
@node Per iniziare
@chapter Per iniziare
@c example heaps are larger than strictly necessary so that readers
@c who use them more extensively are less likely to exhaust memory
Ecco @pmg{} in azione, partendo dal prompt (@samp{$})
della shell @command{bash}:
@verbatim
$ truncate -s 4096000 heap.pma
$ export GAWK_PERSIST_FILE=heap.pma
$ gawk 'BEGIN{un_valore = 47}'
$ gawk 'BEGIN{un_valore += 7; print un_valore}'
54
@end verbatim
@noindent
Per prima cosa, il comando @command{truncate} crea un @dfn{file sparso}
vuoto (tutto a zeri binari) in cui @pmg{} immagazzinerà le variabili
dello script; la sua dimensione dev'essere un multiplo della dimensione
di una pagina del sistema di paginazione (4@tie{}KB).
Poi, il comando @command{export} imposta una variabile di ambiente
che consente a @pmg{} di trovare il file sparso; se @gwk{} @emph{non}
trova questa variabile d'ambiente, la memoria persistente non è
attivata.
Il terzo comando esegue uno script AWK di una riga che inizializza la
variabile @code{un_valore}, la quale sarà poi salvata nel file sparso, e
in questo modo ``sopravviverà'' al programma interpretato che l'ha
inizializzata.
Infine, il quarto comando invoca @pmg{} utilizzando uno script AWK
di una riga @emph{differente} dal precedente, il quale incrementa e
visualizza script @code{un_valore}.
La riga di output mostra che in effetti @pmg{} ha ``ricordato''
@code{un_valore} utilizzandolo in script differenti e non correlati
fra loro. (Se nel file eseguibile @gwk{} trovato nel vostro
cammino di ricerca @env{$PATH} non è disponibile la funzionalità
di memoria persistente, l'output dell'esempio visto sopra sarebbe
@samp{7} invece che @samp{54}. @xref{Installazione}). Per non usare
la persistenza finché non lo si desideri ancora, si può impedire a
@gwk{} di trovare il nome del file sparso, dando il comando
@command{unset GAWK_PERSIST_FILE}. Per ``dimenticare'' del tutto
le variabili dello script, basta cancella il file sparso che le
contiene.
@sp 2
Attivare e disattivare la memoria persistente usando i comandi
@command{export} e @command{unset} per gestire la variabile d'ambiente
richiede attenzione: dimenticando di usare @command{unset} quando
non si vuole più usare la memoria persistente si possono generare errori
e confusione. Fortunatamente @command{bash} consente di
passare a un programma le variabili d'ambiente più selettivamente,
comando per comando:
@verbatim
$ rm heap.pma # ripartire da zero
$ unset GAWK_PERSIST_FILE # eliminare la variabile d'ambiente
$ truncate -s 4096000 heap.pma # creare il nuovo file sparso
$ GAWK_PERSIST_FILE=heap.pma gawk 'BEGIN{un_valore = 47}'
$ gawk 'BEGIN{un_valore += 7; print un_valore}'
7
$ GAWK_PERSIST_FILE=heap.pma gawk 'BEGIN{un_valore += 7; print un_valore}'
54
@end verbatim
@noindent
La prima invocazione di @gwk{} premette la variabile d'ambiente
speciale nella riga di comando, prima di chiamare il comando @gwk{},
e quindi attiva @pmg{}. La seconda invocazione di @gwk{}, tuttavia,
@emph{non} vede la variabile d'ambiente, e quindi non utilizza la
variabile dello script contenuta nel file sparso. La terza invocazione
di @gwk{} vede la variabile d'ambiente speciale, e quindi usa la variabile
dello script contenuta nel file sparso.
Mentre la situazione è già migliorata rispetto all'uso di variabili
d'ambiente ``generali'', il passaggio di variabili d'ambiente col
metodo visto sopra è prolisso e rozzo. Definire un @dfn{alias} di shell
permette di immettere meno caratteri e di semplificare la
visualizzazione:
@verbatim
$ alias pm='GAWK_PERSIST_FILE=heap.pma'
$ pm gawk 'BEGIN{print ++un_valore}'
55
$ pm gawk 'BEGIN{print ++un_valore}'
56
@end verbatim
@c @page
@sp 3
@c ==================================================================
@node Esempi
@chapter Esempi
Il primo esempio usa @pmg{} per analizzare le parole contenute nel
testo dei libri di Mark Twain @cite{Tom Sawyer} e @cite{Huckleberry Finn}
[nell'originale inglese] disponibili in
@c
@url{https://gutenberg.org/files/74/74-0.txt}
@c
e
@c
@url{https://gutenberg.org/files/76/76-0.txt}.
@c
Per prima cosa, i caratteri non-alfabetici sono convertiti nel
carattere ``a capo'' [@dfn{newline}] (in modo che ogni riga contenga al
massimo una parola), e il testo è poi fatto diventare tutto in
caratteri minuscoli.
@verbatim
$ tr -c a-zA-Z '\n' < 74-0.txt | tr A-Z a-z > sawyer.txt
$ tr -c a-zA-Z '\n' < 76-0.txt | tr A-Z a-z > finn.txt
@end verbatim
È facile contare la frequenza delle parole, usando i vettori
associativi di AWK. @pmg{} rende persistenti tali vettori, in
modi da evitare di rileggere nuovamente tutta la raccolta di
testi dati ogni volta che ci poniamo una nuova domanda
(``leggi una volta, analizza felicemente da allora in poi''):
@verbatim
$ truncate -s 100M twain.pma
$ export GAWK_PERSIST_FILE=twain.pma
$ gawk '{ts[$1]++}' sawyer.txt # crea lista parole
$ gawk 'BEGIN{print ts["work"], ts["play"]}' # domanda frequenza
92 11
$ gawk 'BEGIN{print ts["necktie"], ts["knife"]}' # domanda frequenza
2 27
@end verbatim
@noindent
Il comando @command{truncate} genera un file sparso sufficiente a
contenere tutti i dati che dovrebbe poi contenere, più un abbondante
spazio ulteriore. (Come vedremo più avanti, nel @ref{File sparsi}, non
c'è spreco di risorse). Il comando @command{export} assicura che le
successive invocazioni di @gwk{} attiveranno @pmg{}. Il primo comando
@pmg{} immagazzina la frequenza con cui ricorrono le parole nel
libro @cite{Tom Sawyer} nel vettore associativo @code{ts[]}.
Poiché tale vettore è persistente, i successivi comandi @pmg{} possono
utilizzarlo senza dover rileggere il file di input.
Espandere la nostra analisi per includere un secondo libro è facile.
Creiamo un nuovo vettore associativo @code{hf[]} con le frequenza delle
parole contenute in @cite{Huckleberry Finn}:
@verbatim
$ gawk '{hf[$1]++}' finn.txt
@end verbatim
@noindent
A questo punto si può liberamente accedere ai dati relativi a
entrambi i libri, in maniera semplice ed efficiente, senza
dover ogni volta analizzare nuovamente la raccolta di testi
in input:
@verbatim
$ gawk 'BEGIN{print ts["river"], hf["river"]}'
26 142
@end verbatim
Rendendo AWK più interattivo, @pmg{} invita ad effettuare delle
``conversazioni'' a piacere con i dati disponibili. Se ci interessa
vedere quali parole in @cite{Finn} sono assenti da @cite{Sawyer},
le risposte (fra cui ``flapdoodle,'' ``yellocution,'' e
``sockdolager'') sono facili da trovare:
@verbatim
$ gawk 'BEGIN{for(w in hf) if (!(w in ts)) print w}'
@end verbatim
@c also: doxolojer meedyevil ridicklous dingnation gumption cavortings
@c phrenology [words about slavery] shakespeare camelopard ope
@c mesmerism sapheads disremember consekens prevarication
@c missionaryin cannibal nebokoodneezer sentimentering palavering
Le voci riguardo alla morte di Mark Twain possono risultare essere
esagerate. Se egli pubblicherà nuovi libri in futuro, sarà facile
incorporarli nella nostra analisi incrementalmente, uno alla volta.
I benefici in termini di efficienza di un tale modo
di procedere incrementale per alcuni compiti di AWK, come l'analisi
di file di log, sono trattati [in inglese] in
@url{https://queue.acm.org/detail.cfm?id=3534855} e nel saggio
sullo stesso tema, citato là e più sotto, nella sezione
@ref{Prestazioni}.
@c @url{http://nvmw.ucsd.edu/nvmw2022-program/nvmw2022-data/nvmw2022-paper35-final_version_your_extended_abstract.pdf}.
Esercizio: Lo script AWK ``Markov'' riportato a pagina 79 del libro
di Kernighan & Pike @cite{The Practice of Programming} [disponibile
online in inglese] genera un testo a caso, ma che ricorda quello dei
libri di un dato autore, usando una semplice tecnica di modellazione
statistica. Questo script consiste di una fase di ``apprendimento''
o ``addestramento'' seguita da una fase di emissione di output.
Si usi @pmg{} per separare le due fasi e per consentire al modello
statistico di aggiungere incrementalmente nuovi libri a quelli già
presenti.
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Il nostro secondo esempio prende in esame un altro tema, che è un
punto di forza di AWK, l'analisi di dati. Per semplicità creeremo
due piccoli file contenenti dati numerici.
@verbatim
$ printf '1\n2\n3\n4\n5\n' > A.dat
$ printf '5\n6\n7\n8\n9\n' > B.dat
@end verbatim
@noindent
Uno script AWK @emph{non}-persistente può calcolare le statistiche
sommarie di base:
@verbatim
$ cat sommario_convenzionale.awk
1 == NR { min = max = $1 }
min > $1 { min = $1 }
max < $1 { max = $1 }
{ sum += $1 }
END { print "min: " min " max: " max " media: " sum/NR }
$ gawk -f sommario_convenzionale.awk A.dat B.dat
min: 1 max: 9 media: 5
@end verbatim
Volendo usare @pmg{} per fare lo stesso, per prima cosa occorre
creare un file sparso che contenga le variabili del nostro script
AWK e dire a @pmg{} dove sono memorizzate, utilizzando la solita
variabile d'ambiente:
@verbatim
$ truncate -s 10M statistiche.pma
$ export GAWK_PERSIST_FILE=statistiche.pma
@end verbatim
@noindent
@pmg{} richiede di modificare lo script AWK visto sopra per
assicurarsi che le variabili @code{min} e @code{max} siano
inizializzate una volta sola, al primo utilizzo del file sparso
e @emph{non} ogni volta che eseguiamo lo script. Inoltre,
mentre variabili definite dallo script come @code{min} mantengono
il loro valore a ogni successiva invocazione di @pmg{}, le variabili
proprie di AWK, come @code{NR} sono inizializzate di nuovo a ogni
invocazione di @pmg{} e quindi non possono essere usate nello
stesso modo. Ecco una versione dello script modificata per
@pmg{}:
@verbatim
$ cat sommario_persistente.awk
! init { min = max = $1; init = 1 }
min > $1 { min = $1 }
max < $1 { max = $1 }
{ sum += $1; ++n }
END { print "min: " min " max: " max " media: " sum/n }
@end verbatim
@noindent
Si noti la regola differente nella prima riga, e l'introduzione
della variabile @code{n} al posto di @code{NR}. Quando viene usata
con @pmg{}, questa nuova logica di inizializzazione supporta lo
stesso tipo di trattamento incrementale già visto nel precedente
scenario di analisi del testo. Per esempio, possiamo inserire
i nostri file in input uno alla volta:
@verbatim
$ gawk -f sommario_persistente.awk A.dat
min: 1 max: 5 media: 3
$ gawk -f sommario_persistente.awk B.dat
min: 1 max: 9 media: 5
@end verbatim
@noindent
Come ci aspettavamo, dopo che la seconda invocazione di @pmg{}
ha letto il secondo file, l'output è lo stesso che si ottiene
dallo script non-persistente, che legge entrambi i file in
input.
Esercizio: Modificare gli script AWK usati sopra per calcolare
anche la mediana e la moda (o le mode), sia usando @gwk{} convenzionale
che @pmg{}. (La mediana è il valore di mezzo di una lista ordinata;
se la lista contiene un numero pari di elementi, va calcolata la
media dei due numeri centrali. La moda è il valore (o i valori) che
sono presenti con maggiore frequenza in una lista).
@c heaps not portable across machines, use only with same gawk executable (?)
@c refer to gawk docs for portability constraints on heaps
@c can use only one heap at a time
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Il nostro terzo e ultimo insieme di esempi mostra come @pmg{}
consente di mantenere in un file sparso sia i dati che le
@emph{funzioni} definite dall'utente, le quali, quindi, possono
essere, in seguito, liberamente utilizzate in script AWK indipendenti
fra loro.
@c ADR doesn't like return in count() below
@c TK: it was put there for a reason:
@c $ truncate -s 10M funzioni.pma
@c $ export GAWK_PERSIST_FILE=funzioni.pma
@c $ gawk 'function count(A,t) {for(i in A)t++; return t}'
@c $ gawk 'BEGIN { a["x"] = 4; a["y"] = 5; a["z"] = 6 }'
@c $ gawk 'BEGIN { print count(a) }'
@c 3
@c $ gawk 'BEGIN { delete a }'
@c $ gawk 'BEGIN { print count(a) }'
@c [!!blank line, not zero!!]
@c $
La sequenza di comandi che segue invoca @pmg{} per creare e poi
utilizzare una funzione definita dall'utente. Le successive
invocazioni utilizzano parecchi script AWK, tra loro differenti,
che comunicano attraverso il file sparso. Ogni invocazione può
aggiungere funzioni definite dall'utente e aggiungere o togliere
dati al file sparso, a beneficio delle successive invocazioni
di @pmg{}.
@smallformat
@verbatim
$ truncate -s 10M funzioni.pma
$ export GAWK_PERSIST_FILE=funzioni.pma
$ gawk 'function conta(A,t) {for(i in A)t++; return ""==t?0:t}'
$ gawk 'BEGIN { a["x"] = 4; a["y"] = 5; a["z"] = 6 }'
$ gawk 'BEGIN { print conta(a) }'
3
$ gawk 'BEGIN { delete a["x"] }'
$ gawk 'BEGIN { print conta(a) }'
2
$ gawk 'BEGIN { delete a }'
$ gawk 'BEGIN { print conta(a) }'
0
$ gawk 'BEGIN { for (i=0; i<47; i++) a[i]=i }'
$ gawk 'BEGIN { print conta(a) }'
47
@end verbatim
@end smallformat
@noindent
Il primo comando @pmg{} crea la funzione definita dall'utente
@code{conta()}, che restituisce il numero di elementi in un dato
vettore associativo; si noti che la variabile @code{t} è locale
a @code{conta()}, e non globale [ossia vale ``0'' ad ogni
invocazione di @code{conta()}].
Il successivo comando @pmg{} riempie un vettore associativo
persistente con tre elementi; non sorprende quindi che la chiamata
a @code{conta()} nel seguente comando @pmg{} trovi questi tre
elementi. I due comandi @pmg{} seguenti rispettivamente cancellano
un elemento del vettore e stampano il contatore, ora ridotto a due.
I due comandi seguenti cancellano i restanti elementi del vettore,
e quindi viene stampato un contatore a zero.
Infine, gli ultimi due comandi @pmg{} riempiono il vettore con
47 elementi, e visualizzano il contatore aggiornato.
@c I could be persuaded to leave the polinomial example as an
@c exercise, offering to send my answer to readers upon request.
Il seguente script di shell invoca più volte @pmg{} per creare
un insieme di funzioni definite dall'utente che effettuano delle
operazioni di base su polinomi di secondo grado: calcolo del
valore in un dato punto, calcolo del discriminante, e utilizzo
della formula quadratica per trovare le radici.
Dopo di questo, l'espressione @math{x^2 + x - 12} viene
scomposta come @math{(x - 3)(x + 4)}.
@smallformat
@verbatim
#!/bin/sh
rm -f polinomi.pma
truncate -s 10M polinomi.pma
export GAWK_PERSIST_FILE=polinomi.pma
gawk 'function q(x) { return a*x^2 + b*x + c }'
gawk 'function p(x) { return "q(" x ") = " q(x) }'
gawk 'BEGIN { print p(2) }' # valuta e stampa
gawk 'BEGIN{ a = 1; b = 1; c = -12 }' # nuovi coefficienti
gawk 'BEGIN { print p(2) }' # valuta/stampa ancora
gawk 'function d(s) { return s * sqrt(b^2 - 4*a*c)}'
gawk 'BEGIN{ print "discriminante (deve> essere >=0): " d(1)}'
gawk 'function r(s) { return (-b + d(s))/(2*a)}'
gawk 'BEGIN{ print "root: " r( 1) " " p(r( 1)) }'
gawk 'BEGIN{ print "root: " r(-1) " " p(r(-1)) }'
gawk 'function abs(n) { return n >= 0 ? n : -n }'
gawk 'function sgn(x) { return x >= 0 ? "- " : "+ " } '
gawk 'function f(s) { return "(x " sgn(r(s)) abs(r(s))}'
gawk 'BEGIN{ print "factor: " f( 1) ")" }'
gawk 'BEGIN{ print "factor: " f(-1) ")" }'
rm -f polinomi.pma
@end verbatim
@end smallformat
@noindent
@c @page
@sp 3
@c ==================================================================
@node Prestazioni
@chapter Prestazioni
Questo capitolo spiega parecchi vantaggi prestazionali che si possono
ottenere dall'implementazione della memorie persistente in @pmg{},
mostra come una regolazione del Sistema Operativo che si sta usando
migliori talvolta le prestazioni e presenta delle misure sperimentali
di prestazione. Per restare sul pratico, useremo esempi da un
Sistema Operativo GNU/Linux---programmi di utilità GNU in ambiente
OS Linux---ma i principi valgono anche per gli altri sistemi operativi
moderni.
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@node Accesso in tempo costante a vettori
@section Accesso in tempo costante a vettori
@pmg{} conserva l'efficienza dell'accesso ai dati quando delle
strutture dei dati sono create da uno script e utilizzate più
tardi da uno script differente.
Si consideri il vettore associativo usato per analizzare i libri
di Mark Twain nel @ref{Esempi}. Abbiamo creato i vettori @code{ts[]}
ed @code{hf[]} leggendo i file @file{sawyer.txt} e @file{finn.txt}.
Se @i{N} denota il volume totale di questi file, la costruzione
del vettore associativo richiede un tempo proporzionale a
@i{N}, ossia il ``tempo atteso è @i{O(N)}'' nel gergo dell'analisi
asintotica. Se @i{W} è il numero di parole differenti nei file
di input, la dimensione dei vettori associativi sarà proporzionale
a @i{W}, o @i{O(W)}. L'accesso a singoli elementi di vettore
richiede solo il tempo @emph{costante} o tempo atteso @i{O(1)}
non @i{O(N)} o @i{O(W)}, poiché @gwk{} implementa i vettori come
tabelle hash.
@c how much larger is N than W for the Twain texts?
@c % wc -w sawyer.txt finn.txt
@c 77523 sawyer.txt
@c 120864 finn.txt
@c 198387 total
@c % cat sawyer.txt finn.txt | sort | uniq | wc -w
@c 10447
@c
@c #words is 19x larger than #uniquewords
@c
@c Note that the total number of English words in existence is fixed,
@c so as the size of a corpus increases without bound, the ratio of
@c vocabulary size to corpus size tends toward zero.
Il vantaggio di prestazioni di @pmg{} nasce quando script
differenti creano e utilizzano vettori associativi.
Ritrovare un elemento di un vettore persistente creato da un
precedente programma @pmg{}, come fatto più sopra in
@c
@verb{|BEGIN{print ts["river"], hf["river"]}|},
@c
richiede ancora un tempo @i{O(1)}, che è asintoticamente superiore
alle alternative. Ricostruire ingenuamente i vettori a partire
dai file di input originali in ogni programma @gwk{} che accede
ai vettori richiederebbe, naturalmente, un tempo @i{O(N)}---un
costo molto alto, se i testi della raccolta in input sono numerosi.
Scaricare i vettori su dei file e quindi caricarli nuovamente quando
necessario ridurrebbe il tempo di preparazione all'accesso a
@i{O(W)}. Ciò può essere in pratica un miglioramento notevole.
@i{N} è all'incirca 19 volte più ampio di @i{W} nel nostro esempio
con la raccolta di testi di Twain. In ogni caso, @i{O(W)} rimane
molto più lento di @i{O(1)}, che si ottiene usando @pmg{}.
Come vedremo nel @ref{Risultati}, la differenza non è solo teorica.
L'implementazione della memoria persistente utilizzata da @pmg{}
lo mette in grado di evitare una mole di lavoro proporzionale a
a @i{N} o @i{W} nell'accedere agli elementi di un vettore associativo
persistente. Sotto il coperchio, @pmg{} immagazzina le variabili
AWK definite in uno script, come i vettori associativi, in un
frammento persistente di un file mappato in memoria (il file sparso).
Quando uno script AWK utilizza un elemento in un vettore associativo,
@pmg{} lo ricerca nella tabella hash, dalla quale a sua volta si
accede alla memoria del file sparso.
I moderni sistemi operativi implementano i file mappati in memoria
in modo tale che questi accessi alla memoria richiedono una quantità
minima di movimento di dati: solo le parti di file sparso che contengono
i dati richiesti sono ``paginati'' e resi disponibili nella memoria
del programma @pmg{}. Nel caso peggiore, il file sparso non è nella
parte di filesystem già in memoria, e quindi le pagine richieste
devono essere effettivamente lette da disco. Le nostre
analisi asintotiche di efficienza rimangono valide a prescindere
dal fatto che il file sparso sia già in memoria, oppure no.
L'intero file sparso @emph{non} è acceduto solo per poter accedere a
un elemento di un vettore associativo persistente.
La memoria persistente quindi, rende @pmg{} in grado di offrire la
flessibilità di separare la fase di acquisizione dati dalle indagini
vere e proprie, senza la complicazione e il tempo in più richiesti
per serializzare e caricare le strutture dati, e senza sacrificare
un tempo di accesso costante ai vettori associativi, il che rende
gli script AWK comodi e produttivi.
@c Further details on @pmg{}'s persistent heap are available in
@c @url{https://queue.acm.org/detail.cfm?id=3534855}
@c
@c and [excessively long NVMW URL below]
@c
@c @url{http://nvmw.ucsd.edu/nvmw2022-program/nvmw2022-data/nvmw2022-paper35-final_version_your_extended_abstract.pdf}.
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@node Memoria virtuale e file enormi
@section Memoria virtuale e file enormi
I file piccoli raramente turbano il piacere di usare AWK, causando
problemi di prestazioni, con o senza la memoria persistente.
Quando la dimensione delle strutture dati interne dell'interprete
@gwk{} si avvicina alla capacità della memoria fisica, comunque,
per ottenere delle prestazioni accettabili occorre comprendere
il funzionamento dei moderni sistemi operativi e talora operare
delle regolazioni sugli stessi. Fortunatamente @pmg{} offre nuove
possibilità di controllo per utenti attenti alle prestazioni,
che abbiano a che fare con grossi file di dati. Una frase
concisa esprime la filosofia sottesa: Evitare di paginare
favorisce una prestazione ottimale e previene delle perplessità.
I moderni sistemi operativi offrono una @dfn{memoria virtuale} che
si propone di apparire sia maggiore della memoria fisica disponibile
(DRAM -- che è piccola) che più veloce dei dischi fisici disponibili
(che sono lenti). Quando l'utilizzo di memoria di un programma si
avvicina alla capacità massima della DRAM, il sistema della memoria
virtuale, in maniera trasparente, @dfn{pagina} (sposta) i dati del
programma tra la DRAM e un'@dfn{area di swap} su un disco.
La paginazione può degradare le prestazioni in maniera
leggera o in maniera pesante, a seconda di come viene acceduta la
memoria del programma in esecuzione. Accessi causali a grandi
strutture di dati possono provocare una paginazione eccessiva e
dei rallentamenti molto vistosi. Sfortunatamente, le tabelle hash
che stanno dietro ai tipici vettori associativi di AWK richiedono
strutturalmente degli accessi casuali alla memoria, e quindi dei
grossi vettori associativi possono essere una fonte di problemi.
@c ADR comments regarding below, "SSDs alleviate much of the
@c performance problem of hard disks vs. RAM disks."
@c
@c TK replies: When a significant amount of paging to *any*
@c conventional block storage device starts, speed plummets by orders
@c of magnitude. I'd wager that the difference between paging vs. not
@c is larger than the difference between paging to SSD vs. HDD. So
@c while SSDs are faster than HDDs, when paging begins they won't
@c usually make the difference between acceptable vs. unacceptable
@c performance.
@c
@c If you decide to try to find out for yourself, note that on many
@c Linux systems the notorious OOM killer terminates a process well
@c before its anonymous-memory footprint reaches the capacity of DRAM,
@c so it's difficult even to provoke the phenomenon of interest. And
@c paging anonymous memory to swap can be managed differently than
@c paging involving a file-backed memory mapping.
@c
@c Bottom line: For the large majority of purposes, gawk users
@c would be wise to avoid paging entirely, regardless of whether
@c the pm-gawk feature is used.
La memoria persistente cambia le regola a nostro favore: il Sistema
Operativo pagina dei dati verso il @emph{file sparso} di @pmg{}
invece che all'area swap. Ciò non cambierà di molto le prestazioni
se il file sparso risiede in un filesystem tradizionale, su un
disco. Sui sistemi di tipo Unix, tuttavia, si può creare il file
sparso su un filesystem che risiede in memoria, come @file{/dev/shm/},
la qual cosa elimina del tutto la paginazione a dischi esterni,
strutturalmente più lenti. Mettere temporaneamente il file sparso
su un tale filesystem è un accorgimento ragionevole, con due
avvertenze. Primo, occorre tenere a mente che i filesystem che
risiedono in memoria DRAM cessano di esistere quando la macchina
viene riavviata o si impianta. In secondo luogo l'utilizzo di
memoria di @pmg{} non può eccedere la memoria DRAM disponibile,
se si crea il file sparso in un filesystem che risiede nella
memoria DRAM.
La regolazione dei parametri di paginazione del Sistema Operativo
può migliorare le prestazioni se si decide di eseguire @pmg{}
utilizzando un file sparso che risieda su un dispositivo disco
convenzionale. Alcuni sistemi operativi hanno delle abitudini
malsane riguardo alle pagine in memoria modificate (``sporche'',
ovvero diverse rispetto alle pagine corrispondenti presenti
nell'@dfn{area di swap}). Per esempio, il Sistema Operativo può
scrivere le pagine di memoria ``sporche'' così
modificate al file sparso in maniera periodica e/o quando il
Sistema Operativo ritenga che ``troppa'' memoria sia ``sporca''.
Tale ``zelante'' comportamento può degradare notevolmente le
prestazioni, senza recare beneficio alcuno a @pmg{}.
Fortunatamente alcuni Sistemi Operativi consentono di modificare i
parametri di paginazione in modo che la riscrittura sia ``pigra'' e
non ``zelante''. Per Linux, vedere la discussione sui parametro
@code{dirty_*} in
@url{https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html}.
Cambiare questi parametri può prevenire un'inutile paginazione
``zelante'':@footnote{La maniera contorta di effettuare la modifica
utilizzando il comando @command{tee} è spiegata in
@url{https://askubuntu.com/questions/1098059/which-is-the-right-way-to-drop-caches-in-lubuntu}.}
@verbatim
$ echo 100 | sudo tee /proc/sys/vm/dirty_background_ratio
$ echo 100 | sudo tee /proc/sys/vm/dirty_ratio
$ echo 300000 | sudo tee /proc/sys/vm/dirty_expire_centisecs
$ echo 50000 | sudo tee /proc/sys/vm/dirty_writeback_centisecs
@end verbatim
@noindent
La regolazione dei parametri può essere di aiuto sia per @gwk{} non-persistente
che per @pmg{}. [Avviso: La regolazione del Sistema Operativo è un'arte
occulta, potreste essere di parere diverso].
@c sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
@c
@c sudo doesn't convey root privileges to the redirection '>' when calling from cmd line
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@node File sparsi
@section File sparsi
Per risparmiare le risorse di memoria su disco, il file usato come
deposito da @pmg{} dovrebbe essere creato come un @dfn{file sparso}:
un file la cui dimensione logica è maggiore dello spazio che occupa
su disco. I filesystem moderni supportano i file sparsi, che si
possono facilmente definire col comando di utilità @command{truncate},
come visto negli esempi fin qui fatti.
Iniziamo col creare dapprima un file convenzionale @emph{non} sparso,
usando il comando @command{echo}:
@verbatim
$ echo ciao > denso
$ ls -l denso
-rw-rw-r--. 1 me me 5 Aug 5 23:08 denso
$ du -h denso
4.0K denso
@end verbatim
@noindent
Il comando di utilità @command{ls} ci mostra che il file @file{denso}
è lungo cinque byte (quattro per le lettere in ``ciao'' più uno per
il carattere di ritorno a capo). Il comando di utilità @command{du}
ci informa che questo file consuma 4@tie{}KB di memoria---un blocco
su disco, lo spazio minimo occupato su disco da un file di tipo
non sparso. Usiamo poi il comando di utilità @command{truncate}
per creare un file sparso logicamente enorme, e controlliamo quanto
spazio occupa su disco:
@verbatim
$ truncate -s 1T sparso
$ ls -l sparso
-rw-rw-r--. 1 me me 1099511627776 Aug 5 22:33 sparso
$ du -h sparso
0 sparso
@end verbatim
@noindent
Mentre @command{ls} ci restituisce la dimensione logica del file
che ci attendevamo (un Terabyte, o 2 elevato alla quarantesima potenza),
il comando @command{du} ci informa che il file non occupa assolutamente
nessuna memoria. Il filesystem aggiungerà delle risorse di memoria
fisica a fronte di questo file man mano che dei dati vengono scritti
su di esso; se si va a leggere parti del file che non sono state ancora
scritte, il risultato sono dei record a zeri binari.
L'aspetto ``paghi solo quello che usi'' dello spazio occupato
dai file sparsi assicura a chi usa @pmg{} sia la convenienza che
il controllo. Se il vostro filesystem supporta i file sparsi,
utilizzateli creando dei file sparsi di dimensioni abbondanti
per @pmg{}. La loro dimensione logica non costa nulla e l'allocazione
di memoria persistente da parte di @pmg{} continuerà a funzionare
finché le risorse di memoria fisica disponibili nel filesystem
si esauriranno. Se, al contrario, si vuole @emph{evitare} che
un file sparso consumi troppo spazio su disco, basta definire
la sua dimensione iniziale a qualsiasi limite si voglia porre;
il file sparso non utilizzerà più spazio disco di quello
specificato. Se si copiano dei file sparsi usando il comando
GNU @command{cp}, esso crea per default delle copie dei file
che sono a loro volta dei file sparsi.
Se un filesystem è cifrato, può impedire la creazione di file sparsi:
se il valore in chiaro della posizione all'interno del file è tutto
a zeri binari il corrispondente valore cifrato sarà probabilmente
del tutto differente! Effettuare la cifratura a livello della
memoria [ossia codificando il dato prima di scriverlo], invece che
lasciarla fare al filesystem, può offrire un livello accettabile di
sicurezza, pur consentendo al filesystem l'implementazione di
file sparsi.
Talora si potrebbe preferire un file ``normale'' (invece che un file
sparso), per contenere le variabili e le funzioni della memoria
persistente. Per esempio, si potrebbe voler verificare
che l'allocazione di memoria interna di @pmg{} funzioni correttamente
fin quando le informazioni di memoria persistente hanno riempito
completamente l'intero file. Il comando di utilità
@command{fallocate} può preparare il file in questione:
@verbatim
$ fallocate -l 1M megabyte
$ ls -l megabyte
-rw-rw-r--. 1 me me 1048576 Aug 5 23:18 megabyte
$ du -h megabyte
1.0M megabyte
@end verbatim
@noindent
In questo modo si ottiene il Megabyte richiesto, sia
logicamente che fisicamente.
@c UPDATE: search for username in "ls" examples
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@node Persistenza rispetto a durata
@section Persistenza rispetto a durata
Senza dubbio, la regola generale più importante per ottenere delle
buone prestazioni dai computer è, ``paga solo per ciò che
utilizzi''.@footnote{In effetti, questa regola è largamente ignorata,
in maniere sorprendenti. Alcuni ben noti algoritmi, descritti in
libri di testo, continuano la loro esecuzione ben oltre il momento
in cui hanno calcolato tutto ciò che dovevano calcolare. @*
@c
Vedere @url{https://queue.acm.org/detail.cfm?id=3424304}.}
@c
Per applicare questa regola a @pmg{} dobbiamo distinguere due
concetti che sono frequentemente confusi tra loro: la
persistenza e la durata.@footnote{Recentemente il termine
``Memoria Persistente'' è stato talora usato per indicare
un nuovo tipo di memoria non-volatile, indirizzabile byte
per byte---una pratica infelice, che contraddice delle sensate
convenzioni in essere da tempo, e che causa una confusione
ingiustificata. Tale tipo di Memoria-Non-Volatile fornisce
durata [e non persistenza].
La Memoria Persistente qui descritta è invece un'astrazione
software, che non richiede l'utilizzo di Memoria-Non-Volatile
[hardware].
Vedere @url{https://queue.acm.org/detail.cfm?id=3358957}.} (Un terzo
concetto, logicamente distinto è trattato nel @ref{Integrità dei dati}.)
I dati @dfn{persistenti} sopravvivono ai programmi che ne fanno uso,
ma non durano necessariamente per sempre. Per esempio, come spiegato
nella pagina di manuale del comando @command{mq_overview}, le code di
messaggi sono persistenti, perché esistono finché il sistema non viene
spento.
I dati @dfn{durevoli} risiedono su un mezzo fisico che mantiene il
suo contenuto anche se non è connesso a una presa di corrente.
Per esempio, gli hard-disk e i dischi a stato solido contengono
dati durevoli. La confusione sorge perché la persistenza e la
durata sono spesso correlate: i dati nei normali filesystem
che risiedono su HDD o SSD sono tipicamente sia persistenti che
durevoli. La familiarità con comandi di utilità come
@code{fsync()} ed @code{msync()} potrebbe indurre a credere che
la durata è un sottoassieme della persistenza, ma in effetti le
due caratteristiche sono ortogonali: i dati in un'area di swap
sono durevoli, ma non persistenti; i dati in filesystem che
risiedono in memoria come @file{/dev/shm/} sono persistenti,
ma non durevoli.
La durata spesso costa di più rispetto alla persistenza.
Per questo motivo, gli utenti @pmg{} attenti alle prestazioni
pagano il costo in più per ottenere la durata solo qualora la
semplice persistenza non sia sufficiente.
Due modi per evitare il costo della durata sono stati discussi
nel @ref{Memoria virtuale e file enormi}:
Si può porre il file sparso usata da @pmg{} in un filesystem
che risiede nella memoria DRAM del computer, e si può inibire
l'eccessiva riscrittura del file sparso. Espedienti come questo
permettono di togliere il sovraccarico della durata dal tempo
necessario per analisi di dati effettuate in molti stadi.
Questo vale perfino se si desidera che i file sparsi rimangano
poi durevoli: consentono a @pmg{} di funzionare a piena velocità
usando solo la persistenza; forzano per il file utilizzato la
durabilità (usando i comandi @command{cp} e @command{sync} secondo
necessità) dopo che l'output è stato prodotto, in vista della
successiva fase di analisi, e dopo che il programma @pmg{} che
usa il file sparso è terminato.
La sperimentazione utilizzando dati creati in modo casuale aiuta a
comprendere come la persistenza e la durata influenzano le prestazioni.
Si può scrivere un piccolo programma in C o in AWK per generare un
flusso di testo casuale, o si può improvvisare un generatore usando
semplicemente la riga di comando:
@verbatim
$ openssl rand --base64 1000000 | tr -c a-zA-Z '\n' > casuale.dat
@end verbatim
@noindent
Modificando la dimensione degli input casuali si può, per esempio,
trovare quando le prestazioni ``vanno sotto terra'', perché l'uso di
memoria di @pmg{} eccede la capacità della memoria DRAM del computer
e la paginazione ha inizio.
@c TODO:
@c virtual *machines* / cloud machines can make performance hard to measure repeatably
@c here we assume good old fashioned OS install directly on "bare metal"
Le sperimentazioni richiedono un'attenzione meticolosa, specie quando
il file sparso si trova in un filesystem che risiede nella memoria del
computer. Non tener conto della cache del filesystem presente nella
memoria DRAM può facilmente fuorviare nell'interpretazione dei
risultati e rendere inaffidabile la ripetibilità. Fortunatamente
il Sistema Operativo Linux ci permette di svuotare la cache del
filesystem e quindi simulare una condizione di ``partenza a freddo''
quale si avrebbe subito dopo una ripartenza del computer.
In tale situazione, l'accesso ai file normali sulla memoria durevole
avverrebbe leggendo dal dispositivo fisico, e non dalla cache in
memoria. Una spiegazione [in inglese] riguardo ai comandi
@command{sync} e @file{/proc/sys/vm/drop_caches} si trova in @*
@c
@url{https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html}.
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@node Esperimenti
@section Esperimenti
Lo script di C-shell (@command{csh}) che segue illustra i concetti e
implementa i suggerimenti presentati in questo capitolo.
È servito per produrre i risultati trattati nel @ref{Risultati},
è stato eseguito in una ventina di minuti, su un laptop non troppo recente.
Si può tagliare/incollare lo script stesso e metterlo in un file,
o scaricarlo da @url{http://web.eecs.umich.edu/~tpkelly/pma/}.
@c TODO: post script to Web site when finalized
Lo script misura le prestazioni di quattro differenti modi di
effettuare delle indagini sulla frequenza delle parole contenute
in una raccolta di testi.
L'approccio ingenuo, leggere la raccolta di testi e metterla
in un vettore associativo ad ogni esecuzione di una @dfn{query};
scrivere una volta una rappresentazione in formato testo della
tabella con la frequenza delle parole e caricarla all'inizio
di ogni nuova @dfn{query}; usare l'estensione di @gwk{}
@code{rwarray} per scaricare e in seguito ricaricare un vettore
associativo; usare @pmg{} per mantenere un vettore associativo
persistente.
I commenti iniziali spiegano i prerequisiti. Le righe 8--10
gestiscono i parametri in input: la directory in cui eseguire i
test e dove si trovano i file necessari, compreso il file sparso,
il cronometro usato per misurare i tempi di esecuzione,
e altre caratteristiche di prestazione, come l'uso massimo di
memoria e la dimensione dell'input. La dimensione di default
dell'input richiede da parte di @pmg{} l'utilizzo di una memoria
di quasi 3GB, che è sufficientemente grande da produrre risultati
interessanti, e sufficientemente piccola per essere contenuta
nella memoria DRAM del computer, evitando (sui computer in uso
oggi) che sia necessaria la paginazione.
Le righe 13--14 definiscono un cronometro improvvisato.
Due sezioni dello script sono rilevanti se la directory di
default in cui eseguire i test viene cambiata da un filesystem
nella memoria del computer @file{/dev/shm/} a una directory su
un normale filesystem che risiede su un disco esterno.
Le righe 15--17 definiscono il meccanismo per eliminare gli
eventuali dati presenti nella cache DRAM; le righe lines 23--30
impostano i parametri del @dfn{kernel} che scoraggiano il
ricorso frequente alla paginazione.
Le righe 37--70 creano, compilano ed eseguono un piccolo
programma scritto in C, che genera una raccolta di testi casuale.
Il programma è veloce, flessibile e deterministico, ossia genera
sempre lo stesso output casuale, se eseguito con gli stessi
parametri.
Le righe 71--100 testano i quattro differenti approcci ad AWK
a fronte dello stesso input casuale, registrando separatamente
il tempo di preparazione e quello di @dfn{query} del vettore associativo
che contiene la frequenza delle parole.
@c ADR suggests making shell script available on web site
@c TK will do eventually
@sp 1
@c first line of C-shell script can't contain line-number comment
@smallformat
@verbatim
#!/bin/csh -f
# Imposta la var. d'amb. PMG al percorso dell'eseguibile pm-gawk e AWKLIBPATH # 2
# per trovare rwarray.so # 3
# Serve "sudo" per funzionare; si consideri se inserire in /etc/sudoers: # 4
# Defaults:vostro_nome_utente !autentica # 5
echo 'inizio: ' `date` `date +%s` # 6
unsetenv GAWK_PERSIST_FILE # disabilita persistenza per ora # 7
set dir = '/dev/shm' # dove si trova il file sparso etc. # 8
set tmr = '/usr/bin/time' # volendo, disponibile anche "time" della shell # 9
set isz = 1073741824 # dimensione input, 1GB # 10
# set isz = 100000000 # input piccolo per fare test veloci # 11
cd $dir # tick/tock/tyme sotto sono timer improvvisati, errore ~2ms # 12
alias tick 'set t1 = `date +%s.%N`' ; alias tock 'set t2 = `date +%s.%N`' # 13
alias tyme '$PMG -v t1=$t1 -v t2=$t2 "BEGIN{print t2-t1}"' # 14
alias tsync 'tick ; sync ; tock ; echo "sync time: " `tyme`' # 15
alias drop_caches 'echo 3 | sudo tee /proc/sys/vm/drop_caches' # 16
alias snd 'tsync; drop_caches' # 17
echo "pm-gawk: $PMG" ; echo 'std gawk: ' `which gawk` # 18
echo "run dir: $dir" ; echo 'pwd: ' `pwd` # 19
echo 'dir content:' ; ls -l $dir |& $PMG '{print " " $0}' # 20
echo 'timer: ' $tmr ; echo 'AWKLIBPATH: ' $AWKLIBPATH # 21
echo 'Parametri Sistema Operativo:' ; set vm = '/proc/sys/vm/dirty' # 22
sudo sh -c "echo 100 > ${vm}_background_ratio" # rimettere questi # 23
sudo sh -c "echo 100 > ${vm}_ratio" # parametri di paginazione # 24
sudo sh -c "echo 1080000 > ${vm}_expire_centisecs" # ai loro valori di default # 25
sudo sh -c "echo 1080000 > ${vm}_writeback_centisecs" # se necessario # 26
foreach d ( ${vm}_background_ratio ${vm}_ratio \ # 27
${vm}_expire_centisecs ${vm}_writeback_centisecs ) # 28
printf " %-38s %7d\n" $d `cat $d` # 29
end # 30
tick ; tock ; echo 'timr ovrhd: ' `tyme` 's (circa 2ms per TK)' # 31
tick ; $PMG 'BEGIN{print "pm-gawk? yes"}' # 32
tock ; echo 'pmg ovrhd: ' `tyme` 's (circa 4-5 ms per TK)' # 33
set inp = 'input.dat' # 34
echo 'input dime. ' $isz # 35
echo "input file: $inp" # 36
set rg = rgen # crea e compila programma C per generare input casuali # 37
rm -f $inp $rg.c $rg # 38
cat <<EOF > $rg.c # 39
// genera N parole casuali, una per riga, nessuna riga bianca # 40
// caratteri sono e.g. 'abcdefg@' dove '@' diventa 'a capo' # 41
#include <stdio.h> # 42
#include <stdlib.h> # 43
#include <string.h> # 44
#define RCH c = a[rand() % L]; # 45
#define PICK do { RCH } while (0) # 46
#define PICKCH do { RCH } while (c == '@') # 47
#define FP(...) fprintf(stderr, __VA_ARGS__) # 48
int main(int argc, char *argv[]) { # 49
if (4 != argc) { # 50
FP("usage: %s charset N seed\n", # 51
argv[0]); return 1; } # 52
char c, *a = argv[1]; size_t L = strlen(a); # 53
long int N = atol(argv[2]); # 54
srand( atol(argv[3])); # 55
if (2 > N) { FP("N == %ld < 2\n", N); return 2; } # 56
PICKCH; # 57
for (;;) { # 58
if (2 == N) { PICKCH; putchar(c); putchar('\n'); break; } # 59
if ('@' == c) { putchar('\n'); PICKCH; } # 60
else { putchar( c ); PICK; } # 61
if (0 >= --N) break; # 62
} # 63
} # 64
EOF # 65
gcc -std=c11 -Wall -Wextra -O3 -o $rg $rg.c # 66
set t = '@@@@@@@' ; set c = "abcdefghijklmnopqrstuvwxyz$t$t$t$t$t$t" # 67
tick ; ./$rg "$c" $isz 47 > $inp ; tock ; echo 'gen time: ' `tyme` # 68
echo "input file: $inp" # 69
echo 'input wc: ' `wc < $inp` ; echo 'input uniq: ' `sort -u $inp | wc` # 70
snd ############################################################################ # 71
tick ; $tmr $PMG '{n[$1]++}END{print "output: " n["foo"]}' $inp # 72
tock ; echo 'T ingenuo O(N): ' `tyme` ; echo '' # 73
rm -f rwa # 74
snd ############################################################################ # 75
echo '' # 76
tick ; $tmr $PMG -l rwarray '{n[$1]++}END{print "writea",writea("rwa",n)}' $inp # 77
tock ; echo 'T rwarray prepara O(N): ' `tyme` ; echo '' # 78
snd # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 79
tick ; $tmr $PMG -l rwarray 'BEGIN{print "reada",reada("rwa",n); \ # 80
print "output: " n["foo"]}' # 81
tock ; echo 'T rwarray query O(W): ' `tyme` ; echo '' # 82
rm -f ft # 83
snd ############################################################################ # 84
tick ; $tmr $PMG '{n[$1]++}END{for(w in n)print n[w], w}' $inp > ft # 85
tock ; echo 'T freqtbl prepara O(N): ' `tyme` ; echo '' # 86
snd # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 87
tick ; $tmr $PMG '{n[$2] = $1}END{print "output: " n["foo"]}' ft # 88
tock ; echo 'T freqtbl query O(W): ' `tyme` ; echo '' # 89
rm -f heap.pma # 90
snd ############################################################################ # 91
truncate -s 3G heap.pma # allargare se serve # 92
setenv GAWK_PERSIST_FILE heap.pma # 93
tick ; $tmr $PMG '{n[$1]++}' $inp # 94
tock ; echo 'T pm-gawk prepara O(N): ' `tyme` ; echo '' # 95
snd # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 96
tick ; $tmr $PMG 'BEGIN{print "output: " n["foo"]}' # 97
tock ; echo 'T pm-gawk query O(1): ' `tyme` ; echo '' # 98
unsetenv GAWK_PERSIST_FILE # 99
snd ############################################################################ # 100
echo 'Nota: tutte le righe di output sopra dovrebbero essere identiche' ; echo '' # 101
echo 'contenuto dir:' ; ls -l $dir |& $PMG '{print " " $0}' # 102
echo '' ; echo 'memoria usata:' # 103
foreach f ( rwa ft heap.pma ) # compressione molto lenta, la omettiamo # 104
echo " $f " `du -BK $dir/$f` # `xz --best < $dir/$f | wc -c` 'bytes xz' # 105
end # 106
echo '' ; echo 'fine: ' `date` `date +%s` ; echo '' # 107
@end verbatim
@end smallformat
@c @page
@sp 3
@c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@node Risultati
@section Risultati
Eseguendo lo script nel @ref{Esperimenti} con i parametri di default
in un laptop non troppo recente ha dato i risultati riassunti nella
tabella che segue. Esperimenti più ampi, non riferiti qui, conducono
a risultati qualitativamente simili. Occorre tenere presente che le
misurazioni di prestazione sono spesso influenzate da fattori
apparentemente irrilevanti. Per esempio, il programma in esecuzione
può avere il vantaggio di una CPU raffreddata meglio; ulteriori
esecuzioni possono essere effettuati con una CPU più calda, e di
conseguenza subire gli effetti delle variazioni di @dfn{clock}
messe in atto dall'apparato di regolazione termica di un moderno
processore. Molto più in generale, le misurazioni di prestazione
sono faccende delicate. Se si eseguono degli script, per i quali
la facilità di preparazione prevale sulla necessità di esecuzione
veloce, la funzione propria delle misurazioni di prestazione è
quella di testare qualitativamente delle ipotesi come quelle
derivanti da un'analisi asintotica, e di fornire un'idea approssimata
di quando i vari approcci possibili siano preferibili in pratica.
@page
@verbatim
tempo es. massimo uso memoria
AWK script (sec) memoria (K) intermedia (K)
ingenuo O(N) 242,132 2.081.360 n/a
rwarray prep. O(N) 250,288 2.846.868 156.832
rwarray query O(W) 11,653 2.081.444
freqtbl prep. O(N) 288,408 2.400.120 69.112
freqtbl query O(W) 11,624 2.336.616
pm-gawk prep. O(N) 251,946 2.079.520 2.076.608
pm-gawk query O(1) 0,026 3.252
@end verbatim
@sp 1
I risultati sono consistenti con l'analisi asintotica nel
@ref{Accesso in tempo costante a vettori}. Tutti e quattro gli approcci
richiedono circa quattro minuti per la lettura dei dati in input.
L'approccio ingenuo deve farlo ogni volta che si effettua una
@dfn{query}, ma gli altri tre approcci costruiscono un unico vettore
associativo, usato per ognuna delle successive @dfn{query}.
Gli approcci @code{freqtbl} ed @code{rwarray} costruiscono un vettore
associativo di parole, lo scaricano su un file intermedio, che
rileggono prima di ogni @dfn{query}; il primo dei due lo fa manualmente
mentre l'altro usa un'estensione @gwk{}. Entrambi possono eseguire
una nuova @dfn{query} in una decina di secondi, non in quattro minuti.
Come previsto dall'analisi asintotica, eseguire un lavoro in un tempo
proporzionale al numero di parole nel vettore è preferibile a eseguire
il lavoro in un tempo proporzionale alla dimensione della raccolta di
testi in input: il tempo @i{O(W)} è minore di @i{O(N)}.
E, sempre come previsto, le @dfn{query} di @pmg{}'s a tempo costante
sono ancor più veloci, all'incirca di un paio di ordini di grandezza.
Per i calcoli qui presi in esame, @pmg{} fa la differenza fra un
batter d'occhio e un tempo di risposta sufficientemente lungo perché
la mente dell'utente cominci a pensare ad altro.
Mentre @code{freqtbl} ed @code{rwarray} ricostruiscono un vettore
associativo, prima di accedere a un elemento dello stesso,
@pmg{} immagazzina nella memoria persistente un vettore associativo
pronto per l'uso. Questo è il motivo per cui il suo file intermedio
(il file sparso) è molto più grande degli altri due file intermedi,
e la ragione per cui il file sparso è quasi grande quanto il massimo
uso di memoria da parte di @pmg{} nella fase di preparazione del
vettore associativo, mentre l'uso di memoria è molto piccolo quando
usato da una @dfn{query} che accede a un singolo elemento del
vettore.
Il vantaggio che deriva dall'avere un file sparso grande è il
tempo di accesso @i{O(1)} invece che @i{O(W)}---un classico
compromesso fra tempo e spazio. Se la memoria su disco è una
risorsa scarsa, tutti e tre i file intermedi possono essere compressi,
quello @code{freqtbl} di un fattore all'incirca 2,7x, @code{rwarray}
all'incirca 5,6x, e @pmg{} all'incirca 11x, usando il comando
@command{xz}. La compressione usa molto la memoria ed è lenta, un
altro compromesso fra tempo e spazio.
@c @page
@sp 3
@c ==================================================================
@node Integrità dei dati
@chapter Integrità dei dati
Contrattempi come le mancanze di corrente, gli errori del @dfn{kernel},
i bug negli script, e i refusi sulla riga-dei-comandi possono causare
danni ai vostri dati, ma ci sono delle precauzioni che servono a
diminuire questi rischi. Negli scenari che prevedono l'uso di script
basta di solito creare un backup dei file importanti, in tempi
appropriati. Per quanto semplice questo sembri, è necessario
prestare attenzione per ottenere una protezione genuina e per
ridurre il costo del backup. Quel che segue è una maniera prudente
e poco costosa di creare un backup del file sparso fra un uso e
l'altro:
@verbatim
$ nome_backup=sparso_bkup`date +%s`
$ cp --reflink=always sparso.pma $nome_backup.pma
$ chmod a-w $nome_backup.pma
$ sync
$ touch $nome_backup.fatto
$ chmod a-w $nome_backup.fatto
$ sync
$ ls -l sparso*
-rw-rw-r--. 1 me me 4096000 Aug 6 15:53 sparso.pma
-r--r--r--. 1 me me 0 Aug 6 16:16 sparso_bkup1659827771.fatto
-r--r--r--. 1 me me 4096000 Aug 6 16:16 sparso_bkup1659827771.pma
@end verbatim
@noindent
La marcatura temporale aggiunta al nome nei file di backup rende
facile trovare la copia più recente se il file sparso è danneggiato,
anche se l'informazione sull'ultima data di modifica sia stata
inavvertitamente modificata.
@c TODO: sync individual files above instead of globally (?)
@c First carefully check what sync does in both cases
@c using strace, verify that "sync [file]" is correct.
@c Also check whether non-GNU/Linux offers fine-grained
@c sync command. Cygwin? Solaris?
L'opzione @command{--reflink} del comando @command{cp} riduce sia
l'utilizzo di memoria su disco che il tempo richiesto per effettuare
la copia. Allo stesso modo un cui i file sparsi offrono della
memoria su disco del tipo ``paga solo per ciò che utilizzi'',
la copia di tipo reflink offre della memoria del tipo
``paga solo per ciò che @emph{modifichi}''.@footnote{La chiamata-a-sistema
che implementa la copiatura di tipo reflink è descritta nella
pagina di manuale del comando @command{ioctl_ficlone}.}
Una copia reflink condivide memoria col file originale.
Il filesystem garantisce che ulteriori modifiche a uno dei
due file non riguarderà l'altro. La copia con reflink non
è disponibile per tutti i filesystem; al momento è supportata
dai filesystem di tipo XFS, BtrFS, e OCFS2.@footnote{L'opzion
@command{--reflink} crea copie di un file sparso mantenendolo
tale. Se la copiatura con reflink non è disponibile, si dovrebbe
usare invece l'opzione @command{--sparse=always}.}
Fortunatamente è possibile installare, per esempio, un filesystem
XFS @emph{all'interno di un file ordinario} in qualche altro
filesystem, come @code{ext4}.@footnote{Vedere
@url{https://www.usenix.org/system/files/login/articles/login_winter19_08_kelly.pdf}.}
@c The @command{filefrag} utility reveals how the storage allocated to
@c the two files changes if they diverge.
Dopo aver creato una copia di backup del file sparso, usiamo il
comando @command{sync} per forzare la scrittura dalla memoria a
un disco. In caso contrario la copia potrebbe risiedere sono nella
memoria volatile DRAM---nella cache del filesystem---dove una
caduta del sistema o una mancanza di corrente potrebbero
corromperla.@footnote{In alcuni Sistemi Operativi il comando
@command{sync} non garantisce molto, ma in ambiente Linux
@command{sync} termina sono quando tutti i dati di filesystem
sono stati scritti su una memoria durevole.
Se nel vosgtro sistema @command{sync} è inaffidabile, si può
scrivere un piccolo programma in C che invochi la chiamata-a-sistema
@code{fsync()} per forzare la scrittura di un file. Per maggiore
sicurezza, è meglio chiamare @code{fsync()} per ogni directory
a livello superiore nel percorso del file (@code{realpath()})
fino a giungere alla @dfn{root}.} Dopo aver usato @command{sync}
sul backup, creiamo e forziamo la scrittura con @command{sync} di
un file ``indicatore di successo'' con una estensione @file{.fatto}
per prevenire un pericoloso evento, che potrebbe succedere:
ci può essere una mancanza di corrente @emph{mentre} un file
di backup è copiato dal file sparso originale, lasciando uno dei
file (o entrambi) corrotti in memoria---una possibilità
particolarmente preoccupante, per lavori che vengono eseguiti
senza un operatore presente. Dopo una ripartenza, ogni file
@file{.fatto} attesta che il corrispondente backup è andato a
buon fine, facilitando il riconoscimento del backup riuscito
più recente.
@c TODO: ".done" -> ".ready" so ls alphabetizes nicely (?)
Per finire, se si è seriamente intenzionati a ovviare a
eventuali problemi hardware/software si deve essere
``addestrati come per combattere'' testando il vostro
hardware/software contro dei problemi che realisticamente possono
presentarsi. Per un realistico test dei problemi correlati
alle mancanze di corrente, vedere [in inglese]
@c @url{https://cacm.acm.org/magazines/2020/9/246938-is-persistent-memory-persistent/fulltext}
@c
@c and
@url{https://queue.acm.org/detail.cfm?id=3400902}.
@c @page
@sp 3
@c ==================================================================
@node Ringraziamenti
@chapter Ringraziamenti
@c UPDATE: make sure nobody is overlooked
Haris Volos, Zi Fan Tan e Jianan Li hanno sviluppato una versione
prototipo persistente di @gwk{}, a partire da una diramazione
del codice sorgente di @gwk{}.
I consigli che ho ricevuto dal manutentore di @gwk{},
Arnold Robbins, e che ho inoltrato a loro, sono risultati essere
molto utili. Robbins, inoltre, ha implementato, documentato e
testato @pmg{} per la versione ufficiale di @gwk{}; strada facendo,
egli ha suggerito numerosi miglioramenti per l'allocatore di
memoria @code{pma} che sta dietro a @pmg{}. Corinna Vinschen
ha suggerito altri miglioramenti a @code{pma} e testato @pmg{}
in ambiente Cygwin. Nelson H.@: F.@: Beebe ha fornito accesso
a macchine Solaris durante la fase di test. Robbins, Volos, Li,
Tan, Jon Bentley e Hans Boehm hanno rivisto le bozze di questo
manuale utente, e le loro osservazioni sono state utili.
Bentley ha suggerito l'esempio minimo/massimo/media
nel @ref{Esempi}, e anche l'esercizio di rendere persistente
lo script ``Markov'', tratto dal testo di Kernighan e Pike.
Volos ha fornito e testato i suggerimenti sulla regolazione dei
parametri del Sistema Operativo nella @ref{Memoria virtuale e file enormi}.
Stan Park ha fornito degli approfondimenti riguardo a
memoria virtuale, filesystem e programmi di utilità.
@c ==================================================================
@c ==================================================================
@c ==================================================================
@node Installazione
@appendix Installazione
@c UPDATE below or remove this section if it's obsolete
@gwk{} 5.2 con la Memoria Persistente dovrebbe essere disponibile a
settembre 2022; lo si troverà in @url{http://ftp.gnu.org/gnu/gawk/}.
Se la versione 5.2 non è ancora rilasciata, la diramazione sorgente
@dfn{master} sotto git è disponibile in
@c
@url{http://git.savannah.gnu.org/cgit/gawk.git/snapshot/gawk-master.tar.gz}.
@c
Scompattate il file di distribuzione, eseguite i comandi
@command{./bootstrap.sh}, @command{./configure}, @command{make}, e
@command{make check}, e poi sarete in grado di testare qualcuno degli
esempi presentati sopra. Col passare del tempo le versioni
5.2 e successive di @gwk{}, che includono @pmg{} saranno incluse
nei pacchetti di gestione del software delle maggiori distribuzioni
GNU/Linux. Quindi, in futuro, @pmg{} sarà disponibile
nel comando @gwk{} di default per tali sistemi.
@c ADR comments on above, "run ./bootstrap.sh, ./configure ..."
@c TK replies: I haven't been doing this. Neither the README nor the
@c INSTALL in the gawk tarball mention bootstrap.sh. If it's
@c important, shouldn't they? The top of bootstrap.sh says its
@c purpose is "to avoid out-of-date issues in Git sandboxes."
@c When a neurodivergent source code control system requires us to
@c write shell scripts to work around the problems that it creates
@c gratuitously, the universe is trying to tell us something about
@c it.
@c official gawk:
@c http://ftp.gnu.org/gnu/gawk/ [where to look for 5.2 after release]
@c https://www.skeeve.com/gawk/gawk-5.1.62.tar.gz [doesn't support persistent functions]
@c http://git.savannah.gnu.org/cgit/gawk.git/snapshot/gawk-master.tar.gz [if 5.2 isn't released yet]
@c http://git.savannah.gnu.org/cgit/gawk.git [ongoing development]
@c ==================================================================
@node Debugging
@appendix Debugging
@c TODO: ADR: @cite -> @ref to info file below
Per bug non correlati alla persistenza, vedere la documentazione
@gwk{}, e.g., @cite{GAWK: Effective AWK Programming},
disponibile in @url{https://www.gnu.org/software/gawk/manual/}.
[La versione italiana dello stesso libro è disponibile in
@url{https://sites.google.com/view/gawkdoc-it/home-page}.]
Se @pmg{} non si comporta secondo le attese, dovreste per prima
cosa domandarvi se state usando il file sparso che intendevate
usare; usare il file sbagliato è un errore comune.
Altre feconde sorgenti di bug per principianti sono il fatto che
una regola @code{BEGIN} è eseguita ogni volta che si invoca
@pmg{}, il che non è sempre ciò che veramente si vuole, nonché
il fatto che le variabili predefinite di AWK, come per esempio
@code{NR} sono sempre inizializzate a zero ogni volta che si
invoca il programma. Vedere la trattazione dell'inizializzazione
per lo script minimo/massimo/media nel @ref{Esempi}.
Se si sospetta che un bug sia connesso con la persistenza in @pmg{},
si può impostare una variabile d'ambiente che farà sì che il codice
@code{pma}, che si occupa della persistenza, invii dei messagi
diagnostici più numerosi; per i dettagli, si veda la documentazione
principale di @gwk{}.
@c or the @code{pma} documentation at
@c @url{http://web.eecs.umich.edu/~tpkelly/pma/}.
Programmatori: Si può ricompilare @gwk{} abilitando delle asserzioni,
il che causerà degli estesi controlli di integrità all'interno
del codice @code{pma}. Assicuratevi che il sorgente @file{pma.c}
sia compilato @emph{senza} il flag @code{-DNDEBUG}, quando si
compila @gwk{} usando il comando @command{make}.
Il programma eseguibile che ne risulta dovrebbe essere testato
con input piccoli, perché i controlli di integrità possono
rallentare di molto l'esecuzione.
Se qualche asserzione fallisce, significa che c'è un bug
da qualche parte in @pmg{}. Tali bug vanno segnalati a me
(Terence Kelly) come pure seguendo le procedure indicate
nella documentazione principale di @gwk{}. Specificate quale
versione di @gwk{} si sta usando, e cercate di fornire uno
script piccolo e semplice che permetta di riprodurre
il bug in maniera affidabile.
@c @page
@sp 3
@c ==================================================================
@node Storia
@appendix Storia
La funzionalità di Memoria Persistente in @pmg{} si basa su un nuovo
Allocatore di Memoria Persistente, @code{pma}, le cui specifiche
di progettazione [in inglese] sono descritte in
@url{https://queue.acm.org/detail.cfm?id=3534855}. È istruttivo
ripercorrete le fasi dell'evoluzione che ha condotto a
@code{pma} e @pmg{}.
Ho preparato parecchi script AWK durante le ricerche per la mia
tesi di laurea sul Web caching, la maggior parte dei quali analizzava
file di log provenienti da server Web e da cache Web. Avere a
disposizione @gwk{} con la Memoria Persistente avrebbe reso questi
script più piccoli, più veloci, e più facili da scrivere, ma allora
non ero neppure in grado di immaginare che @pmg{} sarebbe stato possibile.
Quindi ho scritto un mucchio di codice inefficiente e tedioso che
manualmente creava e ricaricava variabili AWK utilizzando file di
testo. Un decennio doveva passare prima che i miei colleghi e io
iniziassimo a mettere insieme i pezzi che hanno reso possibile
script che usano la Memoria Persistente, e un altro decennio sarebbe
passato prima che @pmg{} fosse reso in grado di operare.
All'incirca nel 2011, mentre lavoravo agli HP Labs, ho sviluppato
una piattaforma di calcolo distribuito tollerante agli errori,
chiamata ``Ken'', che conteneva un allocatore di Memoria Persistente
che assomigliava a una versione semplificata di @code{pma}: rendeva
disponibile una interfaccia in linguaggio C simile a @code{malloc()}
e allocava memoria tramite una mappatura della memoria, che era
mantenuta anche su un file. L'esperienza fatta con Ken mi convinse
che l'astrazione software della Memoria Persistente è molto
attraente, rispetto alle alternative, per gestire dati persistenti
(p.es., database relazionali e depositi chiave-valore).
Sfortunatamente, l'allocatore di memoria di Ken è così profondamente
interconnesso col resto di Ken da risultare praticamente
inseparabile; per poter fruire dei benefici della Memoria
Persistente di Ken occorreva ``comprare'' anche una vasta serie di
altro complicato software. Quali che fossero i suoi altri pregi,
Ken non è l'ideale per servire da esempio riguardo ai benefici
della Memoria Persistente in se stessa.
Un altro aspetto convoluto di Ken era un meccanismo per resistere
ai crash del computer che, in retrospettiva, può essere visto come
un'implementazione a livello di utente di quello che fa la chiamata
a sistema @code{msync()} resistente a un crash.
Il primo sforzo di estrazione del dopo-Ken ha estratto il meccanismo
di resistenza ai crash, e l'ha implementato nel kernel Linux, e il
risultato è stato definito ``failure-atomic @code{msync()}''
(FAMS -- @code{msync()} resistente a un crash). FAMS rafforza il
significato del normale comando @code{msync()} garantendo che lo stato
permanente di un file mappato in memoria rifletta sempre la più
recente chiamata a @code{msync()}, perfino in presenza di eventi
come una mancanza di corrente e di crash sia a livello di programma
che di Sistema Operativo. Il prototipo originale del FAMS nel
kernel Linux è descritto in un saggio di Park @dfn{et al.} negli
atti del convegno EuroSys 2013. I miei colleghi e io abbiamo in
seguito implementato FAMS in parecchie maniere differenti,
fra cui i filesystem (FAST 2015) e le librerie a livello
di utente.
La mia implementazione più recente di FAMS, che utilizza la
funzionalità di copiatura reflink, descritta in altra parte di
questo manuale, è ora alla base di una nuova funzionalità di
resistenza ai crash nel venerabile e onnipresente comando GNU
@command{dbm} (@command{gdbm}) per gestire dei database
(@url{https://queue.acm.org/detail.cfm?id=3487353}).
Negli ultimi anni la mia attenzione è tornata sui vantaggi
della Memoria Persistente nella programmazione, che è
diventata un argomento ``caldo'' grazie alla disponibilità
commerciale di memoria non-volatile indirizzabile a byte
(che, per confondere le idee, è oggi etichettata come
``Memoria Persistente''). L'astrazione software della
Memoria Persistente e il corrispondente stile di programmazione
sono perfettamente compatibili con dei computer
@emph{convenzionali}---macchine non dotate di memoria non-volatile
e neppure di altro software e hardware particolari.
Ho scritto alcuni saggi per spiegare questo punto, per esempio
[in inglese]
@url{https://queue.acm.org/detail.cfm?id=3358957}.
A inizio 2022 ho scritto un nuovo allocatore di memoria persistente,
a sé stante, @code{pma}, per facilitare l'uso della Memoria Persistente
su hardware convenzionale. L'interfaccia @code{pma} è compatibile
con la chiamata a sistema @code{malloc()} e, a differenza
dell'allocatore presente in Ken, @code{pma} non è collegato a un
particolare meccanismo di resistenza ai crash. Usare @code{pma}
è facile e, almeno per alcuni, piacevole.
Ken è stato integrato in prototipi derivanti sia dall'interprete
di Javascript V8 che da un interprete Scheme, e quindi è parso
naturale domandarsi se @code{pma} avrebbe analogamente potuto
potenziare un linguaggio interpretato di script.
GNU AWK è stata una scelta naturale perché il codice sorgente
è ordinato, e perché @gwk{} ha un solo manutentore principale,
con una mente aperta riguardo all'aggiunta di nuove funzionalità.
Jianan Li, Zi Fan Tan, Haris Volos, e io abbiamo iniziato a prendere
in esame la persistenza per @gwk{} alla fine del 2021. Mentre io
stavo scrivendo @code{pma}, essi hanno preparato un prototipo di
@pmg{} a partire da una diramazione della distribuzione
sorgente ufficiale di @gwk{}.
L'esperienza con il prototipo ha confermato la convenienza e i
benefici per le prestazioni di @pmg{}, e a partire dalla primavera
del 2022 Arnold Robbins ha iniziato a implementare la persistenza
nella versione ufficiale di @gwk{}. La funzionalità di Memoria
Persistente nel comando @gwk{} ufficiale è lievemente differente da
quella del prototipo: la prima usa una variabile d'ambiente per
comunicare all'interprete il nome del file sparso, mentre l'altra
usava un'opzione obbligatoria da inserire nella riga-di-comando.
Per molti aspetti, comunque, le due implementazioni sono simili.
Una descrizione del prototipo, con anche delle misurazioni di
prestazioni è disponibile in
@url{http://nvmw.ucsd.edu/nvmw2022-program/nvmw2022-data/nvmw2022-paper35-final_version_your_extended_abstract.pdf}.
@c lessons learned [these are smallish ideas]
@c compatibility with malloc
@c make programmer do *nothing*
@c components (pma) are easier to sell than monoliths (Ken)
@c open source offers more impact than research
@c work with colleagues who Think Different from one another
@sp 2
Mi piacciono parecchie cose di @pmg{}. Non è invadente; man mano
che acquisite familiarità ed esperienza, scivola sullo sfondo
della vostra programmazione. È semplice sia come concetto che come
implementazione e, cosa ancora più importante, semplifica i vostri
script; molto del suo valore si apprezza non tanto nel codice che
mette in grado di scrivere, quanto nel codice che consente di
eliminare. È tutto ciò (e anche più) di cui avevo bisogno nella mia
tesi di laurea vent'anni fa. Guardando ai fatti, sembra ispirare
la creatività di coloro che l'hanno adottato per primi. Sono curioso
di vedere quali nuove finalità di utilizzo si possono trovare
per esso.
@c ==================================================================
@c ==================================================================
@c ==================================================================
@bye
@c ==================================================================
@c ==================================================================
@c ==================================================================
|