Address Sanitizier invokes OOM-killer - c++

I am trying to use Address Sanitizer, but the kernel keeps killing my process due to excessive memory usage. Without Address Sanitizer the process runs just fine.
The program is compiled for arm-v7a using gcc-8.2.1 with
-fno-omit-frame-pointer
-fsanitize=address
-fsanitize-recover=all
-fdata-sections
-ffunction-sections
-fPIC
I am starting the process as follows:
ASAN_OPTIONS=debug=1:verbosity=0:detect_leaks=0:abort_on_error=0:halt_on_error=0:check_initialization_order=1:allocator_may_return_null=1 ./Launcher
Is there a way to reduce the memory footprint of the Address Sanitizer? Unfortunately, enabling swap is not an option.
This is the kernel log as printed by dmesg:
[512792.413376] Launcher invoked oom-killer: gfp_mask=0x400dc0(GFP_KERNEL_ACCOUNT|__GFP_ZERO), order=0, oom_score_adj=0
[512792.424695] CPU: 3 PID: 7786 Comm: Launcher Tainted: G W 5.4.1 #1
[512792.432821] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[512792.439455] Backtrace:
[512792.442039] [<8010eb1c>] (dump_backtrace) from [<8010eee0>] (show_stack+0x20/0x24)
[512792.449721] r7:811d32ec r6:00000000 r5:60070113 r4:811d32ec
[512792.455500] [<8010eec0>] (show_stack) from [<80ba06e8>] (dump_stack+0xbc/0xe8)
[512792.462840] [<80ba062c>] (dump_stack) from [<80257360>] (dump_header+0x64/0x440)
[512792.470343] r10:00000a24 r9:a9a4ce00 r8:00016f9c r7:80e82aac r6:a749fce0 r5:a9a4ce00
[512792.478275] r4:a749fce0 r3:6f25b167
[512792.481958] [<802572fc>] (dump_header) from [<80256364>] (oom_kill_process+0x494/0x4ac)
[512792.490066] r10:00000a24 r9:a9a4c100 r8:00016f9c r7:80e82aac r6:a749fce0 r5:a9a4ce00
[512792.497996] r4:a9a4d264
[512792.500636] [<80255ed0>] (oom_kill_process) from [<80256e8c>] (out_of_memory+0xf8/0x4ec)
[512792.508830] r10:00000a24 r9:a9a4c100 r8:00016f9c r7:8110b640 r6:8110b640 r5:811d8860
[512792.516760] r4:a749fce0
[512792.519405] [<80256d94>] (out_of_memory) from [<802a0910>] (__alloc_pages_nodemask+0xf7c/0x13a4)
[512792.528295] r9:00000000 r8:81107d30 r7:811d5588 r6:0000233c r5:00000000 r4:00000000
[512792.536153] [<8029f994>] (__alloc_pages_nodemask) from [<80285d10>] (__pte_alloc+0x34/0x1ac)
[512792.544697] r10:74b94000 r9:00000000 r8:00000000 r7:a8b9e580 r6:a8b9e580 r5:a7445d28
[512792.552628] r4:a7445d28
[512792.555271] [<80285cdc>] (__pte_alloc) from [<802869c8>] (copy_page_range+0x4ec/0x650)
[512792.563295] r9:00000000 r8:00000000 r7:a8b9e580 r6:a7174f4c r5:a8b9e580 r4:a7445d28
[512792.571148] [<802864dc>] (copy_page_range) from [<801241b8>] (dup_mm+0x470/0x4e0)
[512792.578736] r10:a7174f14 r9:a7174f10 r8:a8b9d680 r7:a7c36420 r6:a7174f4c r5:a8b9e580
[512792.586667] r4:a7835d20
[512792.589307] [<80123d48>] (dup_mm) from [<801255e0>] (copy_process+0x10bc/0x1888)
[512792.596807] r10:a749ff60 r9:ffffffff r8:00000000 r7:a749e000 r6:9d283400 r5:a825c300
[512792.604738] r4:00100000
[512792.607378] [<80124524>] (copy_process) from [<80125fb8>] (_do_fork+0x90/0x750)
[512792.614792] r10:00100000 r9:a749e000 r8:801011c4 r7:a749e000 r6:a749ff60 r5:6f25b167
[512792.622722] r4:00000001
[512792.625362] [<80125f28>] (_do_fork) from [<80126954>] (sys_clone+0x80/0x9c)
[512792.632428] r10:00000078 r9:a749e000 r8:801011c4 r7:00000078 r6:7649e000 r5:6f25b167
[512792.640358] r4:a749e000
[512792.643001] [<801268d4>] (sys_clone) from [<80101000>] (ret_fast_syscall+0x0/0x28)
[512792.650671] Exception stack(0xa749ffa8 to 0xa749fff0)
[512792.655828] ffa0: 54ad00fc 76ffe964 00100011 00000000 54ad00fc 00000000
[512792.664112] ffc0: 54ad00fc 76ffe964 7649e000 00000078 54ad0100 54ad0120 00000001 54ad0280
[512792.672391] ffe0: 00000078 54ad00e8 763d590b 763bf746
[512792.677546] r5:76ffe964 r4:54ad00fc
[512792.681484] Mem-Info:
[512792.683936] active_anon:158884 inactive_anon:15315 isolated_anon:0
active_file:1041 inactive_file:1140 isolated_file:0
unevictable:2224 dirty:8 writeback:1 unstable:0
slab_reclaimable:4553 slab_unreclaimable:4490
mapped:5064 shmem:17635 pagetables:1579 bounce:0
free:56987 free_pcp:173 free_cma:53962
[512792.718450] Node 0 active_anon:635536kB inactive_anon:61260kB active_file:4264kB inactive_file:5460kB unevictable:8896kB isolated(anon):0kB isolated(file):0kB mapped:21056kB dirty:32kB writeback:4kB shmem:70540kB writeback_tmp:0kB unstable:0kB all_unreclaimable? no
[512792.742142] Normal free:226708kB min:3312kB low:4140kB high:4968kB active_anon:635436kB inactive_anon:61260kB active_file:4584kB inactive_file:5652kB unevictable:8896kB writepending:36kB present:1048576kB managed:1015668kB mlocked:0kB kernel_stack:1216kB pagetables:6316kB bounce:0kB free_pcp:192kB local_pcp:0kB free_cma:215848kB
[512792.771461] lowmem_reserve[]: 0 0 0
[512792.775161] Normal: 1651*4kB (UMEC) 839*8kB (UMEC) 495*16kB (UMEC) 221*32kB (UMEC) 78*64kB (UEC) 29*128kB (MC) 1*256kB (U) 40*512kB (C) 35*1024kB (C) 21*2048kB (C) 10*4096kB (C) 2*8192kB (C) 0*16384kB 1*32768kB (C) = 226708kB
[512792.795442] 20243 total pagecache pages
[512792.799391] 0 pages in swap cache
[512792.802816] Swap cache stats: add 0, delete 0, find 0/0
[512792.808232] Free swap = 0kB
[512792.811225] Total swap = 0kB
[512792.814296] 262144 pages RAM
[512792.817288] 0 pages HighMem/MovableOnly
[512792.821232] 8227 pages reserved
[512792.824558] 81920 pages cma reserved
[512792.828247] Tasks state (memory values in pages):
[512792.833057] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
[512792.841890] [ 211] 0 211 9965 1608 67584 0 0 systemd-journal
[512792.851149] [ 224] 0 224 3848 249 16384 0 -1000 systemd-udevd
[512792.860222] [ 317] 0 317 1559 339 12288 0 0 dhclient
[512792.868867] [ 316] 0 316 1559 348 14336 0 0 dhclient
[512792.877508] [ 333] 0 333 1810 856 14336 0 0 haveged
[512792.886061] [ 334] 101 334 4985 261 22528 0 0 systemd-timesyn
[512792.895309] [ 336] 104 336 1342 167 12288 0 0 rpcbind
[512792.903866] [ 368] 106 368 1333 218 12288 0 -900 dbus-daemon
[512792.912684] [ 369] 0 369 6193 356 22528 0 0 rsyslogd
[512792.921327] [ 370] 0 370 2681 178 18432 0 0 systemd-logind
[512792.930490] [ 372] 0 372 1625 158 14336 0 0 cron
[512792.938784] [ 431] 0 431 428 122 10240 0 0 motion_sensor
[512792.947870] [ 560] 0 560 8756 207 18432 0 0 automount
[512792.956597] [ 564] 0 564 1190 172 12288 0 0 login
[512792.964988] [ 566] 0 566 1338 98 12288 0 0 agetty
[512792.973372] [ 572] 0 572 2218 276 16384 0 -1000 sshd
[512792.981664] [ 574] 0 574 946 33 12288 0 0 inputattach
[512792.990569] [ 637] 0 637 3017 379 18432 0 0 systemd
[512792.999122] [ 640] 0 640 3504 402 20480 0 0 (sd-pam)
[512793.007768] [ 653] 0 653 1760 329 12288 0 0 bash
[512793.016057] [ 671] 0 671 2599 1116 18432 0 0 Server.
[512793.025310] [ 732] 0 732 1300 132 12288 0 0 dbus-daemon
[512793.034212] [ 31836] 0 31836 3173 980 22528 0 0 sshd
[512793.042428] [ 31847] 0 31847 422 154 8192 0 0 sftp-server
[512793.051332] [ 5350] 0 5350 2555 351 16384 0 0 sshd
[512793.059631] [ 5452] 0 5452 1793 379 16384 0 0 bash
[512793.067924] [ 5823] 0 5823 2555 350 16384 0 0 sshd
[512793.076216] [ 5833] 0 5833 1760 326 14336 0 0 bash
[512793.084509] [ 6822] 0 6822 792 31 10240 0 0 xinit
[512793.092813] [ 6823] 0 6823 29526 5386 112640 0 0 Xorg
[512793.101103] [ 6827] 0 6827 3655 866 22528 0 0 xterm
[512793.109488] [ 6829] 0 6829 1620 114 14336 0 0 bash
[512793.117784] [ 7256] 0 7256 1549 322 12288 0 0 watch
[512793.126169] [ 7363] 0 7363 127832 56725 520192 0 0 gdb
[512793.134370] [ 7368] 0 7368 281561 93707 1046528 0 0 Launcher
[512793.143613] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),task=Launcher,pid=7368,uid=0
[512793.152974] Out of memory: Killed process 7368 (Launcher) total-vm:1126244kB, anon-rss:365128kB, file-rss:5700kB, shmem-rss:4000kB, UID:0 pgtables:1046528kB oom_score_adj:0
[512793.387824] oom_reaper: reaped process 7368 (Launcher), now anon-rss:0kB, file-rss:0kB, shmem-rss:4000kB

You could reduce some Asan features (or enable them one by one in separate runs):
# Disable UAR error detection (reduces code and heap size)
CFLAGS+='-fsanitize-address-use-after-return=never -fno-sanitize-address-use-after-scope'
export ASAN_OPTIONS="$ASAN_OPTIONS:detect_stack_use_after_return=1"
# Disable inline instrumentation (slower but saves code size)
CFLAGS+='-fsanitize-address-outline-instrumentation'
# Reduce heap quarantine (reduces heap consumption but also lowers chance of UAF detection)
export ASAN_OPTIONS="$ASAN_OPTIONS:quarantine_size_mb=16"
# Do not keep full backtrace of malloc origin (slightly complicates debugging but reduces heap size)
export ASAN_OPTIONS="$ASAN_OPTIONS:malloc_context_size=5"
Compiler options are for Clang but GCC also has similar switches.
As for the swap, we had good experience with enabling compressed swap in RAM.

Related

rte_eth_tx_burst can not send packet out

A dpdk application which generate a few arp request packets and call rte_eth_tx_burst to send them out, some packets are not received by peer NIC port(this can be confirmed by using wireshark to capture the packets from the peer NIC), dpdk-proc-info shows no error count. But before call rte_eth_tx_burst let the app sleep 10s, it can send all the packets.
example codes:
main(){
port_init();
sleep(10);
gen_pkt(mbuf);
rte_eth_tx_burst(mbuf);
}
System setup: Ubuntu 20.04.2 LTS, dpdk-stable-20.11.3, I350 Gigabit Network Connection 1521, igb_uio driver
root#k8s-node:/home/dpdk-stable-20.11.3/build/app# ./dpdk-proc-info -- --xstats
EAL: No legacy callbacks, legacy socket not created
###### NIC extended statistics for port 0 #########
####################################################
rx_good_packets: 10
tx_good_packets: 32
rx_good_bytes: 1203
tx_good_bytes: 1920
rx_missed_errors: 0
rx_errors: 0
tx_errors: 0
rx_mbuf_allocation_errors: 0
rx_q0_packets: 0
rx_q0_bytes: 0
rx_q0_errors: 0
tx_q0_packets: 0
tx_q0_bytes: 0
rx_crc_errors: 0
rx_align_errors: 0
rx_symbol_errors: 0
rx_missed_packets: 0
tx_single_collision_packets: 0
tx_multiple_collision_packets: 0
tx_excessive_collision_packets: 0
tx_late_collisions: 0
tx_total_collisions: 0
tx_deferred_packets: 0
tx_no_carrier_sense_packets: 0
rx_carrier_ext_errors: 0
rx_length_errors: 0
rx_xon_packets: 0
tx_xon_packets: 0
rx_xoff_packets: 0
tx_xoff_packets: 0
rx_flow_control_unsupported_packets: 0
rx_size_64_packets: 4
rx_size_65_to_127_packets: 3
rx_size_128_to_255_packets: 3
rx_size_256_to_511_packets: 0
rx_size_512_to_1023_packets: 0
rx_size_1024_to_max_packets: 0
rx_broadcast_packets: 0
rx_multicast_packets: 10
rx_undersize_errors: 0
rx_fragment_errors: 0
rx_oversize_errors: 0
rx_jabber_errors: 0
rx_management_packets: 0
rx_management_dropped: 0
tx_management_packets: 0
rx_total_packets: 10
tx_total_packets: 32
rx_total_bytes: 1203
tx_total_bytes: 1920
tx_size_64_packets: 32
tx_size_65_to_127_packets: 0
tx_size_128_to_255_packets: 0
tx_size_256_to_511_packets: 0
tx_size_512_to_1023_packets: 0
tx_size_1023_to_max_packets: 0
tx_multicast_packets: 0
tx_broadcast_packets: 32
tx_tso_packets: 0
tx_tso_errors: 0
rx_sent_to_host_packets: 0
tx_sent_by_host_packets: 0
rx_code_violation_packets: 0
interrupt_assert_count: 0
####################################################
root#k8s-node:/home/dpdk-stable-20.11.3/build/app# ./dpdk-proc-info -- --stats
EAL: No legacy callbacks, legacy socket not created
######################## NIC statistics for port 0 ########################
RX-packets: 5 RX-errors: 0 RX-bytes: 785
RX-nombuf: 0
TX-packets: 32 TX-errors: 0 TX-bytes: 1920
Stats reg 0 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 1 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 2 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 3 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 4 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 5 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 6 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 7 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 8 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 9 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 10 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 11 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 12 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 13 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 14 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 15 RX-packets: 0 RX-errors: 0 RX-bytes: 0
Stats reg 0 TX-packets: 0 TX-bytes: 0
Stats reg 1 TX-packets: 0 TX-bytes: 0
Stats reg 2 TX-packets: 0 TX-bytes: 0
Stats reg 3 TX-packets: 0 TX-bytes: 0
Stats reg 4 TX-packets: 0 TX-bytes: 0
Stats reg 5 TX-packets: 0 TX-bytes: 0
Stats reg 6 TX-packets: 0 TX-bytes: 0
Stats reg 7 TX-packets: 0 TX-bytes: 0
Stats reg 8 TX-packets: 0 TX-bytes: 0
Stats reg 9 TX-packets: 0 TX-bytes: 0
Stats reg 10 TX-packets: 0 TX-bytes: 0
Stats reg 11 TX-packets: 0 TX-bytes: 0
Stats reg 12 TX-packets: 0 TX-bytes: 0
Stats reg 13 TX-packets: 0 TX-bytes: 0
Stats reg 14 TX-packets: 0 TX-bytes: 0
Stats reg 15 TX-packets: 0 TX-bytes: 0
############################################################################
update:
Thanks for your response, I modified the codes:
main(){
uint32_t port_mask = 0x1;
port_init();
check_all_ports_link_status(port_mask);
gen_pkt(mbuf);
rte_eth_tx_burst(mbuf);
}
got the print logs:
Checking link status...............................
done
Port0 Link Up. Speed 1000 Mbps - full-duplex
I think the NIC should have initallized completely, but the peer NIC port still missed a lot of packets.
In most working cases the Physical NIC is enumerated for Duplex (full/half), speed (1, 10, 25, 40, 50, 100, 200) and negotiated for (auto/disable) within 1 second. Anything exceeding 2 or 3 seconds is the sign of connected machine or switch not able to negotiated with Duplex, speed or auto-negotiation. Hence the recommendation is
update the driver, firmware on both sides if the interfaces are NIC
Test out the different connection cable as link-sense might not be reaching properly
in case of hub or switch try fixing speed and auto-negotiation.
I do not recommend changing from FULL duplex to Half duplex (as it could be cable or SFI issue).
As temporary work around for the time being you can use rte_eth_link_get which also states it might need It might need to wait up to 9 seconds.
Note: easy way to test if it is cable issue is running DPDK on both ends to check time required for link to be up.
Modified Code Snippet:
main(){
port_init();
RTE_ETH_FOREACH_DEV(portid) {
struct rte_eth_link link;
memset(&link, 0, sizeof(link));
do {
retval = rte_eth_link_get_nowait(port, &link);
if (retval < 0) {
printf("Failed link get (port %u): %s\n",
port, rte_strerror(-retval));
return retval;
} else if (link.link_status)
break;
printf("Waiting for Link up on port %"PRIu16"\n", port);
sleep(1);
} while (!link.link_status);
}
gen_pkt(mbuf);
rte_eth_tx_burst(mbuf);
}
or
main(){
port_init();
RTE_ETH_FOREACH_DEV(portid) {
struct rte_eth_link link;
memset(&link, 0, sizeof(link));
ret = rte_eth_link_get(portid, &link);
if (ret < 0) {
printf("Port %u link get failed: err=%d\n", portid, ret);
continue;
}
gen_pkt(mbuf);
rte_eth_tx_burst(mbuf);
}
It's no surprise that packets can't be sent until the physical link goes up. That takes some time, and one can use rte_eth_link_get() API to automate waiting.

Populating a DataFrame from 2 other DataFrames

I have been trying to combine the information from 2 dataframes into a single new dataframe without luck. I have searched extensively, but still can't find any relevant answer, so apologies if I have missed it in my search.
When creating an investing strategy, among a large set of currencies (more than 50) I have picked the top 5 currencies to invest in for every date (in top_n.csv) and their respective % weight to invest for each currency on each date (in weights.csv).
top_n.csv lools like:
Date 0 1 2 3 4
Aug 12, 2016 bitcoin ethereum 0 0 0
Aug 11, 2016 bitcoin ethereum ripple steem litecoin
Aug 10, 2016 bitcoin ethereum ripple 0 0
Aug 09, 2016 bitcoin ethereum steem ripple ethereum-classic
weights.csv lools like:
Date 0 1 2 3 4
Aug 12, 2016 0.859 0.089 nan nan nan
Aug 11, 2016 0.856 0.092 0.020 0.016 0.016
Aug 10, 2016 0.853 0.093 0.020 nan nan
Aug 09, 2016 0.858 0.086 0.020 0.020 0.017
The DataFrame which I am trying to populate is one which contains same dates (in the index), but has a number of columns corresponding to a larger set of coins (more than 50), like in W.csv.
Is there an efficient way that (for each date) populates the right weights to any currency that has any, and leaves the others at 0? The tricky part is dealing with dates when there are not enough currencies (thus top_n.csv has less than n currencies, and weights.csv has nans in the respective positions).
W.csv lools like:
Date bitcoin ethereum bitcoin-cash ripple litecoin dash neo nem monero ethereum-classic iota qtum omisego lisk cardano zcash bitconnect tether stellar ....
Aug 12, 2016 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ....
Aug 11, 2016 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ....
Aug 10, 2016 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ....
Aug 09, 2016 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ....
My target is to result to a DataFrame that looks like W_all_target, which I attach as would not appear correctly (I have edited it by hand for this question).
I have saved three indicative CSVs as it will help to examine them.
https://drive.google.com/open?id=1olx9ARI0XP5mqbqF1pfRfJyl9wIEWyZj
I am still learning, so I understand this may be a simple question. Sincere thanks!!
Option 0
This is to accommodate the zeros and nans
dates = top_n.index.repeat(top_n.shape[1])
currs = top_n.values.ravel()
wghts = weights.values.ravel()
mask = currs != '0'
reshaped = pd.Series(wghts[mask], [dates[mask], currs[mask]]).unstack(fill_value=0)
W.update(reshaped)
Option 1
reshaped = pd.concat([d.stack() for d in [top_n, weights]], axis=1) \
.reset_index(1, drop=True).set_index(0, append=True)[1].unstack(fill_value=0)
reshaped
0 bitcoin ethereum ethereum-classic litecoin ripple steem
Date
2016-08-09 0.858 0.086 0.017 0.000 0.02 0.020
2016-08-10 0.853 0.093 0.000 0.016 0.02 0.018
2016-08-11 0.856 0.092 0.000 0.016 0.02 0.016
2016-08-12 0.859 0.089 0.000 0.016 0.02 0.015
Option 2
reshaped = pd.Series(
weights.values.ravel(),
[top_n.index.repeat(top_n.shape[1]), top_n.values.ravel()]
).unstack(fill_value=0)
reshaped
bitcoin ethereum ethereum-classic litecoin ripple steem
Date
2016-08-09 0.858 0.086 0.017 0.000 0.02 0.020
2016-08-10 0.853 0.093 0.000 0.016 0.02 0.018
2016-08-11 0.856 0.092 0.000 0.016 0.02 0.016
2016-08-12 0.859 0.089 0.000 0.016 0.02 0.015
Then you should be able to update W with
W.update(reshaped)
W
bitcoin ethereum bitcoin-cash ripple litecoin dash neo nem monero ethereum-classic iota qtum omisego lisk cardano zcash bitconnect tether stellar
Date
2016-08-12 0.859 0.089 0 0.02 0.016 0 0 0 0 0.000 0 0 0 0 0 0 0 0 0
2016-08-11 0.856 0.092 0 0.02 0.016 0 0 0 0 0.000 0 0 0 0 0 0 0 0 0
2016-08-10 0.853 0.093 0 0.02 0.016 0 0 0 0 0.000 0 0 0 0 0 0 0 0 0
2016-08-09 0.858 0.086 0 0.02 0.000 0 0 0 0 0.017 0 0 0 0 0 0 0 0 0

How can I remove junk characters from a file?

I have a file that contains text like the following. How can I remove the "junk" characters like ^[[H using Perl ?
^[[H^[[2J^[(B^[[mtop - 19:25:22 up 69 days, 23:25, 2 users, load average: 2.55, 2.15, 1.83^[(B^[[m^[[39;49m^[[K
Tasks:^[(B^[[m^[[39;49m^[(B^[[m 114 ^[(B^[[m^[[39;49mtotal,^[(B^[[m^[[39;49m^[(B^[[m 1 ^[(B^[[m^[[39;49mrunning,^[(B^[[m^[[39;49m^[(B^[[m 113 ^[(B^[[m^[[39;49msleeping,^[(B^[[m^[[39;49m^[(B^[[m 0 ^[(B^[[m^[[39;49mstopped,^[(B^[[m^[[39;49m^[(B^[[m 0 ^[(B^[[m^[[39;49mzombie^[(B^[[m^[[39;49m^[[K
Cpu(s):^[(B^[[m^[[39;49m^[(B^[[m 18.1%^[(B^[[m^[[39;49mus,^[(B^[[m^[[39;49m^[(B^[[m 0.5%^[(B^[[m^[[39;49msy,^[(B^[[m^[[39;49m^[(B^[[m 0.0%^[(B^[[m^[[39;49mni,^[(B^[[m^[[39;49m^[(B^[[m 81.2%^[(B^[[m^[[39;49mid,^[(B^[[m^[[39;49m^[(B^[[m 0.0%^[(B^[[m^[[39;49mwa,^[(B^[[m^[[39;49m^[(B^[[m 0.0%^[(B^[[m^[[39;49mhi,^[(B^[[m^[[39;49m^[(B^[[m 0.2%^[(B^[[m^[[39;49msi,^[(B^[[m^[[39;49m^[(B^[[m 0.0%^[(B^[[m^[[39;49mst^[(B^[[m^[[39;49m^[[K
em: ^[(B^[[m^[[39;49m^[(B^[[m 16435100k ^[(B^[[m^[[39;49mtotal,^[(B^[[m^[[39;49m^[(B^[[m 3081324k ^[(B^[[m^[[39;49mused,^[(B^[[m^[[39;49m^[(B^[[m 13353776k ^[(B^[[m^[[39;49mfree,^[(B^[[m^[[39;49m^[(B^[[m 196396k ^[(B^[[m^[[39;49mbuffers^[(B^[[m^[[39;49m^[[K
Swap:^[(B^[[m^[[39;49m^[(B^[[m 4194296k ^[(B^[[m^[[39;49mtotal,^[(B^[[m^[[39;49m^[(B^[[m 0k ^[(B^[[m^[[39;49mused,^[(B^[[m^[[39;49m^[(B^[[m 4194296k ^[(B^[[m^[[39;49mfree,^[(B^[[m^[[39;49m^[(B^[[m 1531300k ^[(B^[[m^[[39;49mcached^[(B^[[m^[[39;49m^[[K
^[[6;1H
^[[7m PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND ^[(B^[[m^[[39;49m^[[K
^[(B^[[m22285 root 25 0 4931m 398m 11m S 47.6 2.5 545:13.49 java ^[(B^[[m^[[39;49m
^[(B^[[m19235 root 17 0 1406m 624m 10m S 2.0 3.9 6203:15 java ^[(B^[[m^[[39;49m
^[(B^[[m 1 root 15 0 10368 684 572 S 0.0 0.0 0:09.51 init ^[(B^[[m^[[39;49m
^[(B^[[m 2 root RT -5 0 0 0 S 0.0 0.0 2:02.87 migration/0 ^[(B^[[m^[[39;49m
^[(B^[[m 3 root 34 19 0 0 0 S 0.0 0.0 0:00.27 ksoftirqd/0 ^[(B^[[m^[[39;49m
^[(B^[[m 4 root RT -5 0 0 0 S 0.0 0.0 2:00.50 migration/1 ^[(B^[[m^[[39;49m
^[(B^[[m 5 root 34 19 0 0 0 S 0.0 0.0 0:00.26 ksoftirqd/1 ^[(B^[[m^[[39;49m
^[(B^[[m 6 root RT -5 0 0 0 S 0.0 0.0 2:04.21 migration/2 ^[(B^[[m^[[39;49m
^[(B^[[m 7 root 34 19 0 0 0 S 0.0 0.0 0:00.26 ksoftirqd/2 ^[(B^[[m^[[39;49m
^[(B^[[m 8 root RT -5 0 0 0 S 0.0 0.0 1:52.52 migration/3 ^[(B^[[m^[[39;49m
You can use Term::ANSIColor module,
perl -MTerm::ANSIColor=colorstrip -ne 'print colorstrip $_' file

Assigning Variables from CSV files (or another format) in C++

Hello Stack Overflow world :3 My name is Chris, I have a slight issue.. So I am going to present the issue in this format..
Part 1
I will present the materials & code snippets I am currently working with that IS working..
Part 2
I will explain in my best ability my desired new way of achieving my goal.
Part 3
So you guys think I am not having you do all the work, I will go ahead and present my attempts at said goal, as well as possibly ways research has dug up that I did not fully understand.
Part 1
mobDB.csv Example:
ID Sprite kName iName LV HP SP EXP JEXP Range1 ATK1 ATK2 DEF MDEF STR AGI VIT INT DEX LUK Range2 Range3 Scale Race Element Mode Speed aDelay aMotion dMotion MEXP ExpPer MVP1id MVP1per MVP2id MVP2per MVP3id MVP3per Drop1id Drop1per Drop2id Drop2per Drop3id Drop3per Drop4id Drop4per Drop5id Drop5per Drop6id Drop6per Drop7id Drop7per Drop8id Drop8per Drop9id Drop9per DropCardid DropCardper
1001 SCORPION Scorpion Scorpion 24 1109 0 287 176 1 80 135 30 0 1 24 24 5 52 5 10 12 0 4 23 12693 200 1564 864 576 0 0 0 0 0 0 0 0 990 70 904 5500 757 57 943 210 7041 100 508 200 625 20 0 0 0 0 4068 1
1002 PORING Poring Poring 1 50 0 2 1 1 7 10 0 5 1 1 1 0 6 30 10 12 1 3 21 131 400 1872 672 480 0 0 0 0 0 0 0 0 909 7000 1202 100 938 400 512 1000 713 1500 512 150 619 20 0 0 0 0 4001 1
1004 HORNET Hornet Hornet 8 169 0 19 15 1 22 27 5 5 6 20 8 10 17 5 10 12 0 4 24 4489 150 1292 792 216 0 0 0 0 0 0 0 0 992 80 939 9000 909 3500 1208 15 511 350 518 150 0 0 0 0 0 0 4019 1
1005 FARMILIAR Familiar Familiar 8 155 0 28 15 1 20 28 0 0 1 12 8 5 28 0 10 12 0 2 27 14469 150 1276 576 384 0 0 0 0 0 0 0 0 913 5500 1105 20 2209 15 601 50 514 100 507 700 645 50 0 0 0 0 4020 1
1007 FABRE Fabre Fabre 2 63 0 3 2 1 8 11 0 0 1 2 4 0 7 5 10 12 0 4 22 385 400 1672 672 480 0 0 0 0 0 0 0 0 914 6500 949 500 1502 80 721 5 511 700 705 1000 1501 200 0 0 0 0 4002 1
1008 PUPA Pupa Pupa 2 427 0 2 4 0 1 2 0 20 1 1 1 0 1 20 10 12 0 4 22 256 1000 1001 1 1 0 0 0 0 0 0 0 0 1010 80 915 5500 938 600 2102 2 935 1000 938 600 1002 200 0 0 0 0 4003 1
1009 CONDOR Condor Condor 5 92 0 6 5 1 11 14 0 0 1 13 5 0 13 10 10 12 1 2 24 4233 150 1148 648 480 0 0 0 0 0 0 0 0 917 9000 1702 150 715 80 1750 5500 517 400 916 2000 582 600 0 0 0 0 4015 1
1010 WILOW Willow Willow 4 95 0 5 4 1 9 12 5 15 1 4 8 30 9 10 10 12 1 3 22 129 200 1672 672 432 0 0 0 0 0 0 0 0 902 9000 1019 100 907 1500 516 700 1068 3500 1067 2000 1066 1000 0 0 0 0 4010 1
1011 CHONCHON Chonchon Chonchon 4 67 0 5 4 1 10 13 10 0 1 10 4 5 12 2 10 12 0 4 24 385 200 1076 576 480 0 0 0 0 0 0 0 0 998 50 935 6500 909 1500 1205 55 601 100 742 5 1002 150 0 0 0 0 4009 1
So this is an example of the Spreadsheet I have.. This is what I wish to be using in my ideal goal. Not what I am using right now.. It was done in MS Excel 2010, using Columns A-BF and Row 1-993
Currently my format for working code, I am using manually implemented Arrays.. For example for the iName I have:
char iName[16][25] = {"Scorpion", "Poring", "Hornet", "Familiar", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null"};
Defined in a header file (bSystem.h) now to apply, lets say their health variable? I have to have another array in the same Header with corresponding order, like so:
int HP[16] = {1109, 50, 169, 155, 95, 95, 118, 118, 142, 142, 167, 167, 193, 193, 220, 220};
The issue is, there is a large amount of data to hard code into the various file I need for Monsters, Items, Spells, Skills, ect.. On the original small scale to get certain system made it was fine.. I have been using various Voids in header files to transfer data from file to file when it's called.. But when I am dealing with 1,000+ Monsters and having to use all these variables.. Manually putting them in is kinda.. Ridiculous? Lol...
Part 2
Now my ideal system for this, is to be able to use the .CSV Files to load the data.. I have hit a decent amount of various issues in this task.. Such as, converting the data pulled from Names to a Char array, actually pulling the data from the CSV file and assigning specific sections to certain arrays... The main idea I have in mind, that I can not seem to get to is this;
I would like to be able to find a way to just read these various variables from the CSV file... So when I call upon the variables like:
cout << name << "(" << health << " health) VS. " << iName[enemy] << "(" << HP[enemy] << " health)";
where [enemy] is, it would be the ID.. the enemy encounter is in another header (lSystem.h) where it basically goes like;
case 0:
enemy = 0;
Where 0 would be the first data in the Arrays involving Monsters.. I hate that it has to be order specific.. I would want to be able to say enemy = 1002; so when the combat systems start it can just pull the variables it needs from the enemy with the ID 1002..
I always hit a few different issues, I can't get it to pull the data from the file to the program.. When I can, I can only get it to store int values to int arrays, I have issues getting it to convert the strings to char arrays.. Then the next issue I am presented with is recalling it and the actual saving part... Which is where part 3 comes in :3
Part 3
I have attempted a few different things so far and have done research on how to achieve this.. What I have came across so far is..
I can write a function to read the data from let's say mobDB, record it into arrays, then output it to a .dat? So when I need to recall variables I can do some from the .dat instead of a modifiable CSV.. I was presented with the same issues as far as reading and converting..
I can go the SQL route, but I have had a ton of issues understanding how to pull the data from the SQL? I have a PowerEdge 2003 Server box in my house which I store data on, it does have NavicatSQL Premium set up, so I guess my main 2 questions about the SQL route is, is it possible to hook right into the SQLServer and as I update the Data Base, when the client runs it would just pull the variables and data from the DB? Or would I be stuck compiling SQL files... When it is an online game, I know I will have to use something to transfer from Server to Client, which is why I am trying to set up this early in dev so I have more to build off of, I am sure I can use SQL servers for that? If anyone has a good grasp on how this works I would very much like to take the SQL route..
Attempts I have made are using like, Boost to Parse the data from the CSV instead of standard libs.. Same issues were presented.. I did read up on converting a string to a char.. But the issue lied in once I pulled the data, I couldn't convert it?..
I've also tried the ADO C++ route.. Dead end there..
All in all I have spent the last week or so on this.. I would very much like to set up the SQL server to actually update the variables... but I am open to any working ideas that presents ease of editing and implementing large amounts of data..
I appreciate any and all help.. If anyone does attempt to help get a working code for this, if it's not too much trouble to add comments to parts you feel you should explain? I don't want someone to just give me a quick fix.. I actually want to learn and understand what I am using. Thank you all very much :)
-Chris
Let's see if I understand your problem correctly: You are writing a game and currently all the stats for your game actors are hardcoded. You already have an Excel spreadsheet with this data and you just want to use this instead of the hardcoded header files, so that you can tweak the stats without waiting for a long recompilation. You are currently storing the stats in your code in a column-store fashion, i.e. one array per attribute. The CSV file stores stuff in a row-wise fashion. Correct so far?
Now my understanding of your problem becomes a little blurry. But let's try. If I understand you correctly, you want to completely remove the arrays from your code and directly access the CSV file when you need the stats for some creature? If yes, then this is already the problem. File I/O is incredibly slow, you need to keep this data in main memory. Just keep the arrays, but instead of manually assigning the values in the headers, you have a load function that reads the CSV file when you start the game and loads its contents into the array. You can keep the rest of your code unchanged.
Example:
void load (std::ifstream &csv)
{
readFirstLineAndCheckThatItIsCorrect (csv);
while (!csv.eof())
{
int id;
std::string spriteName;
csv >> id;
csv >> spriteName >> kName[id] >> iName[id] >> LV[id] >> HP[id] >> SP[id] >> ...
Sprite[id] = getSpriteForName (spriteName);
}
}
Using a database system is completely out of scope here. All you need to do is load some data into some arrays. If you want to be able to change the stats without restarting the program, add some hotkey for reloading the CSV file.
If you plan to write an online game, then you still have a long way ahead of you. Even then, SQL is a very bad idea for exchanging data between server and clients because a) it just introduces way too much overhead and b) it is an open invitation for cheaters and hackers because if clients have direct access to your database, you can no longer validate their inputs. See http://forums.somethingawful.com/showthread.php?noseen=0&pagenumber=258&threadid=2803713 for an actual example.
If you really want this to be an online game, you need to design your own communication protocol. But maybe you should read some books about that first, because it really is a complex issue. For instance, you need to hide the latency from the user by guessing on the client side what the server and the other players will most likely do next, and gracefully correct your guesses if they were wrong, all without the player noticing (Dead Reckoning).
Still, good luck on your game and I hope to play it some day. :-)
IMO, the simplest thing to do would be to first create a struct that holds all the data for a monster. Here's a reduced version because I don't feel like typing all those variables.
struct Mob
{
std::string SPRITE, kName, iName;
int ID, LV, HP, SP, EXP;
};
The loading code for your particular format is then fairly simple:
bool ParseMob(const std::string & str, Mob & m)
{
std::stringstream iss(str);
Mob tmp;
if (iss >> tmp.ID >> tmp.SPRITE >> tmp.kName >> tmp.iName
>> tmp.LV >> tmp.HP >> tmp.SP >> tmp.EXP)
{
m = tmp;
return true;
}
return false;
}
std::vector<Mob> LoadMobs()
{
std::vector<Mob> mobs;
Mob tmp;
std::ifstream fin("mobDB.csv");
for (std::string line; std::getline(fin, line); )
{
if (ParseMob(line,tmp))
mobs.emplace_back(std::move(tmp));
}
return mobs;
}

Does multithreading emphasize memory fragmentation?

Description
When allocating and deallocating randomly sized memory chunks with 4 or more threads using openmp's parallel for construct, the program seems to start leaking considerable amounts of memory in the second half of the test-program's runtime. Thus it increases its consumed memory from 1050 MB to 1500 MB or more without actually making use of the extra memory.
As valgrind shows no issues, I must assume that what appears to be a memory leak actually is an emphasized effect of memory fragmentation.
Interestingly, the effect does not show yet if 2 threads make 10000 allocations each, but it shows strongly if 4 threads make 5000 allocations each. Also, if the maximum size of allocated chunks is reduced to 256kb (from 1mb), the effect gets weaker.
Can heavy concurrency emphasize fragmentation that much ? Or is this more likely to be a bug in the heap ?
Test Program Description
The demo program is build to obtain a total of 256 MB of randomly sized memory chunks from the heap, doing 5000 allocations. If the memory limit is hit, the chunks allocated first will be deallocated until the memory consumption falls below the limit. Once 5000 allocations where performed, all memory is released and the loop ends. All this work is done for each thread generated by openmp.
This memory allocation scheme allows us to expect a memory consumption of ~260 MB per thread (including some bookkeeping data).
Demo Program
As this is really something you might want to test, you can download the sample program with a simple makefile from dropbox.
When running the program as is, you should have at least 1400 MB of RAM available. Feel free to adjust the constants in the code to suit your needs.
For completeness, the actual code follows:
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <deque>
#include <omp.h>
#include <math.h>
typedef unsigned long long uint64_t;
void runParallelAllocTest()
{
// constants
const int NUM_ALLOCATIONS = 5000; // alloc's per thread
const int NUM_THREADS = 4; // how many threads?
const int NUM_ITERS = NUM_THREADS;// how many overall repetions
const bool USE_NEW = true; // use new or malloc? , seems to make no difference (as it should)
const bool DEBUG_ALLOCS = false; // debug output
// pre store allocation sizes
const int NUM_PRE_ALLOCS = 20000;
const uint64_t MEM_LIMIT = (1024 * 1024) * 256; // x MB per process
const size_t MAX_CHUNK_SIZE = 1024 * 1024 * 1;
srand(1);
std::vector<size_t> allocations;
allocations.resize(NUM_PRE_ALLOCS);
for (int i = 0; i < NUM_PRE_ALLOCS; i++) {
allocations[i] = rand() % MAX_CHUNK_SIZE; // use up to x MB chunks
}
#pragma omp parallel num_threads(NUM_THREADS)
#pragma omp for
for (int i = 0; i < NUM_ITERS; ++i) {
uint64_t long totalAllocBytes = 0;
uint64_t currAllocBytes = 0;
std::deque< std::pair<char*, uint64_t> > pointers;
const int myId = omp_get_thread_num();
for (int j = 0; j < NUM_ALLOCATIONS; ++j) {
// new allocation
const size_t allocSize = allocations[(myId * 100 + j) % NUM_PRE_ALLOCS ];
char* pnt = NULL;
if (USE_NEW) {
pnt = new char[allocSize];
} else {
pnt = (char*) malloc(allocSize);
}
pointers.push_back(std::make_pair(pnt, allocSize));
totalAllocBytes += allocSize;
currAllocBytes += allocSize;
// fill with values to add "delay"
for (int fill = 0; fill < (int) allocSize; ++fill) {
pnt[fill] = (char)(j % 255);
}
if (DEBUG_ALLOCS) {
std::cout << "Id " << myId << " New alloc " << pointers.size() << ", bytes:" << allocSize << " at " << (uint64_t) pnt << "\n";
}
// free all or just a bit
if (((j % 5) == 0) || (j == (NUM_ALLOCATIONS - 1))) {
int frees = 0;
// keep this much allocated
// last check, free all
uint64_t memLimit = MEM_LIMIT;
if (j == NUM_ALLOCATIONS - 1) {
std::cout << "Id " << myId << " about to release all memory: " << (currAllocBytes / (double)(1024 * 1024)) << " MB" << std::endl;
memLimit = 0;
}
//MEM_LIMIT = 0; // DEBUG
while (pointers.size() > 0 && (currAllocBytes > memLimit)) {
// free one of the first entries to allow previously obtained resources to 'live' longer
currAllocBytes -= pointers.front().second;
char* pnt = pointers.front().first;
// free memory
if (USE_NEW) {
delete[] pnt;
} else {
free(pnt);
}
// update array
pointers.pop_front();
if (DEBUG_ALLOCS) {
std::cout << "Id " << myId << " Free'd " << pointers.size() << " at " << (uint64_t) pnt << "\n";
}
frees++;
}
if (DEBUG_ALLOCS) {
std::cout << "Frees " << frees << ", " << currAllocBytes << "/" << MEM_LIMIT << ", " << totalAllocBytes << "\n";
}
}
} // for each allocation
if (currAllocBytes != 0) {
std::cerr << "Not all free'd!\n";
}
std::cout << "Id " << myId << " done, total alloc'ed " << ((double) totalAllocBytes / (double)(1024 * 1024)) << "MB \n";
} // for each iteration
exit(1);
}
int main(int argc, char** argv)
{
runParallelAllocTest();
return 0;
}
The Test-System
From what I see so far, the hardware matters a lot. The test might need adjustments if run on a faster machine.
Intel(R) Core(TM)2 Duo CPU T7300 # 2.00GHz
Ubuntu 10.04 LTS 64 bit
gcc 4.3, 4.4, 4.6
3988.62 Bogomips
Testing
Once you have executed the makefile, you should get a file named ompmemtest. To query the memory usage over time, I used the following commands:
./ompmemtest &
top -b | grep ompmemtest
Which yields the quite impressive fragmentation or leaking behaviour. The expected memory consumption with 4 threads is 1090 MB, which became 1500 MB over time:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11626 byron 20 0 204m 99m 1000 R 27 2.5 0:00.81 ompmemtest
11626 byron 20 0 992m 832m 1004 R 195 21.0 0:06.69 ompmemtest
11626 byron 20 0 1118m 1.0g 1004 R 189 26.1 0:12.40 ompmemtest
11626 byron 20 0 1218m 1.0g 1004 R 190 27.1 0:18.13 ompmemtest
11626 byron 20 0 1282m 1.1g 1004 R 195 29.6 0:24.06 ompmemtest
11626 byron 20 0 1471m 1.3g 1004 R 195 33.5 0:29.96 ompmemtest
11626 byron 20 0 1469m 1.3g 1004 R 194 33.5 0:35.85 ompmemtest
11626 byron 20 0 1469m 1.3g 1004 R 195 33.6 0:41.75 ompmemtest
11626 byron 20 0 1636m 1.5g 1004 R 194 37.8 0:47.62 ompmemtest
11626 byron 20 0 1660m 1.5g 1004 R 195 38.0 0:53.54 ompmemtest
11626 byron 20 0 1669m 1.5g 1004 R 195 38.2 0:59.45 ompmemtest
11626 byron 20 0 1664m 1.5g 1004 R 194 38.1 1:05.32 ompmemtest
11626 byron 20 0 1724m 1.5g 1004 R 195 40.0 1:11.21 ompmemtest
11626 byron 20 0 1724m 1.6g 1140 S 193 40.1 1:17.07 ompmemtest
Please Note: I could reproduce this issue when compiling with gcc 4.3, 4.4 and 4.6(trunk).
Ok, picked up the bait.
This is on a system with
Intel(R) Core(TM)2 Quad CPU Q9550 # 2.83GHz
4x5666.59 bogomips
Linux meerkat 2.6.35-28-generic-pae #50-Ubuntu SMP Fri Mar 18 20:43:15 UTC 2011 i686 GNU/Linux
gcc version 4.4.5
total used free shared buffers cached
Mem: 8127172 4220560 3906612 0 374328 2748796
-/+ buffers/cache: 1097436 7029736
Swap: 0 0 0
Naive run
I just ran it
time ./ompmemtest
Id 0 about to release all memory: 258.144 MB
Id 0 done, total alloc'ed -1572.7MB
Id 3 about to release all memory: 257.854 MB
Id 3 done, total alloc'ed -1569.6MB
Id 1 about to release all memory: 257.339 MB
Id 2 about to release all memory: 257.043 MB
Id 1 done, total alloc'ed -1570.42MB
Id 2 done, total alloc'ed -1569.96MB
real 0m13.429s
user 0m44.619s
sys 0m6.000s
Nothing spectacular. Here is the simultaneous output of vmstat -S M 1
Vmstat raw data
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
0 0 0 3892 364 2669 0 0 24 0 701 1487 2 1 97 0
4 0 0 3421 364 2669 0 0 0 0 1317 1953 53 7 40 0
4 0 0 2858 364 2669 0 0 0 0 2715 5030 79 16 5 0
4 0 0 2861 364 2669 0 0 0 0 6164 12637 76 15 9 0
4 0 0 2853 364 2669 0 0 0 0 4845 8617 77 13 10 0
4 0 0 2848 364 2669 0 0 0 0 3782 7084 79 13 8 0
5 0 0 2842 364 2669 0 0 0 0 3723 6120 81 12 7 0
4 0 0 2835 364 2669 0 0 0 0 3477 4943 84 9 7 0
4 0 0 2834 364 2669 0 0 0 0 3273 4950 81 10 9 0
5 0 0 2828 364 2669 0 0 0 0 3226 4812 84 11 6 0
4 0 0 2823 364 2669 0 0 0 0 3250 4889 83 10 7 0
4 0 0 2826 364 2669 0 0 0 0 3023 4353 85 10 6 0
4 0 0 2817 364 2669 0 0 0 0 3176 4284 83 10 7 0
4 0 0 2823 364 2669 0 0 0 0 3008 4063 84 10 6 0
0 0 0 3893 364 2669 0 0 0 0 4023 4228 64 10 26 0
Does that information mean anything to you?
Google Thread Caching Malloc
Now for real fun, add a little spice
time LD_PRELOAD="/usr/lib/libtcmalloc.so" ./ompmemtest
Id 1 about to release all memory: 257.339 MB
Id 1 done, total alloc'ed -1570.42MB
Id 3 about to release all memory: 257.854 MB
Id 3 done, total alloc'ed -1569.6MB
Id 2 about to release all memory: 257.043 MB
Id 2 done, total alloc'ed -1569.96MB
Id 0 about to release all memory: 258.144 MB
Id 0 done, total alloc'ed -1572.7MB
real 0m11.663s
user 0m44.255s
sys 0m1.028s
Looks faster, not?
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
4 0 0 3562 364 2684 0 0 0 0 1041 1676 28 7 64 0
4 2 0 2806 364 2684 0 0 0 172 1641 1843 84 14 1 0
4 0 0 2758 364 2685 0 0 0 0 1520 1009 98 2 1 0
4 0 0 2747 364 2685 0 0 0 0 1504 859 98 2 0 0
5 0 0 2745 364 2685 0 0 0 0 1575 1073 98 2 0 0
5 0 0 2739 364 2685 0 0 0 0 1415 743 99 1 0 0
4 0 0 2738 364 2685 0 0 0 0 1526 981 99 2 0 0
4 0 0 2731 364 2685 0 0 0 684 1536 927 98 2 0 0
4 0 0 2730 364 2685 0 0 0 0 1584 1010 99 1 0 0
5 0 0 2730 364 2685 0 0 0 0 1461 917 99 2 0 0
4 0 0 2729 364 2685 0 0 0 0 1561 1036 99 1 0 0
4 0 0 2729 364 2685 0 0 0 0 1406 756 100 1 0 0
0 0 0 3819 364 2685 0 0 0 4 1159 1476 26 3 71 0
In case you wanted to compare vmstat outputs
Valgrind --tool massif
This is the head of output from ms_print after valgrind --tool=massif ./ompmemtest (default malloc):
--------------------------------------------------------------------------------
Command: ./ompmemtest
Massif arguments: (none)
ms_print arguments: massif.out.beforetcmalloc
--------------------------------------------------------------------------------
GB
1.009^ :
| ##::::##:::::::##::::::##::::##::#::::#::::#:::::::::#::::::#:::
| # :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#:::
| # :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#:::
| :# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#:::
| :# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#:::
| :# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| : ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| : ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| :: ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| :: ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::: ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::: ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
| ::: ::# :: :# :::: ::# : ::::# :: :# ::#::::#: ::#:::::: ::#::::::#::::
0 +----------------------------------------------------------------------->Gi
0 264.0
Number of snapshots: 63
Detailed snapshots: [6 (peak), 10, 17, 23, 27, 30, 35, 39, 48, 56]
Google HEAPPROFILE
Unfortunately, vanilla valgrind doesn't work with tcmalloc, so I switched horses midrace to heap profiling with google-perftools
gcc openMpMemtest_Linux.cpp -fopenmp -lgomp -lstdc++ -ltcmalloc -o ompmemtest
time HEAPPROFILE=/tmp/heapprofile ./ompmemtest
Starting tracking the heap
Dumping heap profile to /tmp/heapprofile.0001.heap (100 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0002.heap (200 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0003.heap (300 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0004.heap (400 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0005.heap (501 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0006.heap (601 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0007.heap (701 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0008.heap (801 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0009.heap (902 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0010.heap (1002 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0011.heap (2029 MB allocated cumulatively, 1031 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0012.heap (3053 MB allocated cumulatively, 1030 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0013.heap (4078 MB allocated cumulatively, 1031 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0014.heap (5102 MB allocated cumulatively, 1031 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0015.heap (6126 MB allocated cumulatively, 1033 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0016.heap (7151 MB allocated cumulatively, 1029 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0017.heap (8175 MB allocated cumulatively, 1029 MB currently in use)
Dumping heap profile to /tmp/heapprofile.0018.heap (9199 MB allocated cumulatively, 1028 MB currently in use)
Id 0 about to release all memory: 258.144 MB
Id 0 done, total alloc'ed -1572.7MB
Id 2 about to release all memory: 257.043 MB
Id 2 done, total alloc'ed -1569.96MB
Id 3 about to release all memory: 257.854 MB
Id 3 done, total alloc'ed -1569.6MB
Id 1 about to release all memory: 257.339 MB
Id 1 done, total alloc'ed -1570.42MB
Dumping heap profile to /tmp/heapprofile.0019.heap (Exiting)
real 0m11.981s
user 0m44.455s
sys 0m1.124s
Contact me for full logs/details
Update
To the comments: I updated the program
--- omptest/openMpMemtest_Linux.cpp 2011-05-03 23:18:44.000000000 +0200
+++ q/openMpMemtest_Linux.cpp 2011-05-04 13:42:47.371726000 +0200
## -13,8 +13,8 ##
void runParallelAllocTest()
{
// constants
- const int NUM_ALLOCATIONS = 5000; // alloc's per thread
- const int NUM_THREADS = 4; // how many threads?
+ const int NUM_ALLOCATIONS = 55000; // alloc's per thread
+ const int NUM_THREADS = 8; // how many threads?
const int NUM_ITERS = NUM_THREADS;// how many overall repetions
const bool USE_NEW = true; // use new or malloc? , seems to make no difference (as it should)
It ran for over 5m3s. Close to the end, a screenshot of htop teaches that indeed, the reserved set is slightly higher, going towards 2.3g:
1 [||||||||||||||||||||||||||||||||||||||||||||||||||96.7%] Tasks: 125 total, 2 running
2 [||||||||||||||||||||||||||||||||||||||||||||||||||96.7%] Load average: 8.09 5.24 2.37
3 [||||||||||||||||||||||||||||||||||||||||||||||||||97.4%] Uptime: 01:54:22
4 [||||||||||||||||||||||||||||||||||||||||||||||||||96.1%]
Mem[||||||||||||||||||||||||||||||| 3055/7936MB]
Swp[ 0/0MB]
PID USER NLWP PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
4330 sehe 8 20 0 2635M 2286M 908 R 368. 28.8 15:35.01 ./ompmemtest
Comparing results with a tcmalloc run: 4m12s, similar top stats has minor differences; the big difference is in the VIRT set (but that isn't particularly useful unless you have a very limited address space per process?). The RES set is quite similar, if you ask me. The more important thing to note is parallellism is increased; all cores are now maxed out. This is obviously due to reduced need to lock for heap operations when using tcmalloc:
If the free list is empty: (1) We fetch a bunch of objects from a central free list for this size-class (the central free list is shared by all threads). (2) Place them in the thread-local free list. (3) Return one of the newly fetched objects to the applications.
1 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%] Tasks: 172 total, 2 running
2 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%] Load average: 7.39 2.92 1.11
3 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%] Uptime: 11:12:25
4 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
Mem[|||||||||||||||||||||||||||||||||||||||||||| 3278/7936MB]
Swp[ 0/0MB]
PID USER NLWP PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
14391 sehe 8 20 0 2251M 2179M 1148 R 379. 27.5 8:08.92 ./ompmemtest
When linking the test program with google's tcmalloc library, the executable doesn't only run ~10% faster, but shows greatly reduced or insignificant memory fragmentation as well:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
13441 byron 20 0 379m 334m 1220 R 187 8.4 0:02.63 ompmemtestgoogle
13441 byron 20 0 1085m 1.0g 1220 R 194 26.2 0:08.52 ompmemtestgoogle
13441 byron 20 0 1111m 1.0g 1220 R 195 26.9 0:14.42 ompmemtestgoogle
13441 byron 20 0 1131m 1.1g 1220 R 195 27.4 0:20.30 ompmemtestgoogle
13441 byron 20 0 1137m 1.1g 1220 R 195 27.6 0:26.19 ompmemtestgoogle
13441 byron 20 0 1137m 1.1g 1220 R 195 27.6 0:32.05 ompmemtestgoogle
13441 byron 20 0 1149m 1.1g 1220 R 191 27.9 0:37.81 ompmemtestgoogle
13441 byron 20 0 1149m 1.1g 1220 R 194 27.9 0:43.66 ompmemtestgoogle
13441 byron 20 0 1161m 1.1g 1220 R 188 28.2 0:49.32 ompmemtestgoogle
13441 byron 20 0 1161m 1.1g 1220 R 194 28.2 0:55.15 ompmemtestgoogle
13441 byron 20 0 1161m 1.1g 1220 R 191 28.2 1:00.90 ompmemtestgoogle
13441 byron 20 0 1161m 1.1g 1220 R 191 28.2 1:06.64 ompmemtestgoogle
13441 byron 20 0 1161m 1.1g 1356 R 192 28.2 1:12.42 ompmemtestgoogle
From the data I have, the answer appears to be:
Multithreaded access to the heap can emphasize fragmentation if the employed heap library does not deal well with concurrent access and if the processor fails to execute the threads truly concurrently.
The tcmalloc library shows no significant memory fragmentation running the same program that previously caused ~400MB to be lost in fragmentation.
But why does that happen ?
The best idea I have to offer here is some sort of locking artifact within the heap.
The test program will allocate randomly sized blocks of memory, freeing up blocks allocated early in the program to stay within its memory limit. When one thread is in the process of releasing old memory which is in a heap block on the 'left', it might actually be halted as another thread is scheduled to run, leaving a (soft) lock on that heap block. The newly scheduled thread wants to allocate memory, but may not even read that heap block on the 'left' side to check for free memory as it is currently being changed. Hence it might end up using a new heap block unnecessarily from the 'right'.
This process could look like a heap-block-shifting, where the the first blocks (on the left) remain only sparsely used and fragmented, forcing new blocks to be used on the right.
Lets restate that this fragmentation issue only occurs for me if I use 4 or more threads on a dual core system which can only handle two threads more or less concurrently. When only two threads are used, the (soft) locks on the heap will be held short enough not to block the other thread who wants to allocate memory.
Also, as a disclaimer, I didn't check the actual code of the glibc heap implementation, nor am I anything more than novice in the field of memory allocators - all I wrote is just how it appears to me which makes it pure speculation.
Another interesting read might be the tcmalloc documentation, which states common problems with heaps and multi-threaded access, some of which may have played their role in the test program too.
Its worth noting that it will never return memory to the system (see Caveats paragraph in tcmalloc documentation)
Yes the default malloc (Depending on linux version) does some crazy stuff which fails massively in some multi threaded applications. Specifically it keeps almost per thread heaps (arenas) to avoid locking. This is much faster than a single heap for all threads, but massively memory inefficient (sometimes). You can tune this by using code like this which turns off the multiple arenas (this kills performance so don't do this if you have lots of small allocations!)
rv = mallopt(-7, 1); // M_ARENA_TEST
rv = mallopt(-8, 1); // M_ARENA_MAX
Or as others suggested using various replacements for malloc.
Basically it's impossible for a general purpose malloc to always be efficient as it doesn't know how it's going to be used.
ChrisP.