Commit a3f75b90 authored by Peter W. Draper's avatar Peter W. Draper
Browse files

First attempt to instrument memuse reports in SWIFT

parent 08cca88d
......@@ -155,8 +155,9 @@ int main(int argc, char *argv[]) {
if ((res = MPI_Comm_rank(MPI_COMM_WORLD, &myrank)) != MPI_SUCCESS)
error("Call to MPI_Comm_rank failed with error %i.", res);
/* Make sure messages are stamped with the correct rank. */
/* Make sure messages are stamped with the correct rank and step. */
engine_rank = myrank;
engine_cstep = 0;
if ((res = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN)) !=
MPI_SUCCESS)
......
......@@ -47,7 +47,7 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \
dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \
gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \
chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h
chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h memuse.h
# Common source files
AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
......@@ -59,7 +59,7 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
statistics.c runner_doiact_vec.c profiler.c dump.c logger.c \
part_type.c xmf.c gravity_properties.c gravity.c \
collectgroup.c hydro_space.c equation_of_state.c \
chemistry.c cosmology.c restart.c
chemistry.c cosmology.c restart.c memuse.c
# Include files for distribution, not installation.
nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \
......
......@@ -305,8 +305,8 @@ static void dumpCells_map(struct cell *c, void *data) {
/* Only cells with particles are dumped. */
if (c->count > 0 || c->gcount > 0 || c->scount > 0) {
/* In MPI mode we may only output cells with foreign partners.
* These define the edges of the partitions. */
/* In MPI mode we may only output cells with foreign partners.
* These define the edges of the partitions. */
int ismpiactive = 0;
#if WITH_MPI
ismpiactive = (c->send_xv != NULL);
......@@ -582,69 +582,3 @@ void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells) {
}
#endif /* HAVE_MPI */
/**
* @brief parse the process /proc/self/statm file to get the process
* memory use (in KB). Top field in ().
*
* @param size total virtual memory (VIRT)
* @param resident resident non-swapped memory (RES)
* @param share shared (mmap'd) memory (SHR)
* @param trs text (exe) resident set (CODE)
* @param lrs library resident set
* @param drs data+stack resident set (DATA)
* @param dt dirty pages (nDRT)
*/
void getProcMemUse(long *size, long *resident, long *share, long *trs,
long *lrs, long *drs, long *dt) {
/* Open the file. */
FILE *file = fopen("/proc/self/statm", "r");
if (file != NULL) {
int nscan = fscanf(file, "%ld %ld %ld %ld %ld %ld %ld", size, resident,
share, trs, lrs, drs, dt);
if (nscan == 7) {
/* Convert pages into bytes. Usually 4096, but could be 512 on some
* systems so take care in conversion to KB. */
long sz = sysconf(_SC_PAGESIZE);
*size *= sz;
*resident *= sz;
*share *= sz;
*trs *= sz;
*lrs *= sz;
*drs *= sz;
*dt *= sz;
*size /= 1024;
*resident /= 1024;
*share /= 1024;
*trs /= 1024;
*lrs /= 1024;
*drs /= 1024;
*dt /= 1024;
} else {
error("Failed to read sufficient fields from /proc/self/statm");
}
fclose(file);
} else {
error("Failed to open /proc/self/statm");
}
}
/**
* @brief Print the current memory use of the process. A la "top".
*/
void printProcMemUse() {
long size;
long resident;
long share;
long trs;
long lrs;
long drs;
long dt;
getProcMemUse(&size, &resident, &share, &trs, &lrs, &drs, &dt);
printf("## VIRT = %ld , RES = %ld , SHR = %ld , CODE = %ld, DATA = %ld\n",
size, resident, share, trs, drs);
fflush(stdout);
}
......@@ -49,7 +49,4 @@ void dumpMETISGraph(const char *prefix, idx_t nvtxs, idx_t ncon, idx_t *xadj,
void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells);
#endif
void getProcMemUse(long *size, long *resident, long *share, long *trs,
long *lrs, long *drs, long *dt);
void printProcMemUse();
#endif /* SWIFT_DEBUG_H */
......@@ -61,6 +61,7 @@
#include "gravity.h"
#include "hydro.h"
#include "map.h"
#include "memuse.h"
#include "minmax.h"
#include "parallel_io.h"
#include "part.h"
......@@ -103,6 +104,9 @@ const char *engine_policy_names[] = {"none",
/** The rank of the engine as a global variable (for messages). */
int engine_rank;
/** The current step of the engine as a global variable (for messages). */
int engine_cstep;
/**
* @brief Data collected from the cells at the end of a time-step
*/
......@@ -394,6 +398,7 @@ static void *engine_do_redistribute(int *counts, char *parts,
(void **)&parts_new, alignsize,
sizeofparts * new_nr_parts * engine_redistribute_alloc_margin) != 0)
error("Failed to allocate new particle data.");
memuse_report("parts", sizeofparts * new_nr_parts * engine_redistribute_alloc_margin);
/* Prepare MPI requests for the asynchronous communications */
MPI_Request *reqs;
......@@ -1498,6 +1503,7 @@ void engine_exchange_cells(struct engine *e) {
if (posix_memalign((void **)&pcells, SWIFT_CACHE_ALIGNMENT,
sizeof(struct pcell) * count_out) != 0)
error("Failed to allocate pcell buffer.");
memuse_report("pcells", sizeof(struct pcell) * count_out);
/* Pack the cells. */
cell_next_tag = 0;
......@@ -1568,6 +1574,8 @@ void engine_exchange_cells(struct engine *e) {
sizeof(struct part) * s->size_parts_foreign) != 0)
error("Failed to allocate foreign part data.");
}
memuse_report("parts_foreign", sizeof(struct part) * s->size_parts_foreign);
if (count_gparts_in > s->size_gparts_foreign) {
if (s->gparts_foreign != NULL) free(s->gparts_foreign);
s->size_gparts_foreign = 1.1 * count_gparts_in;
......@@ -1575,6 +1583,8 @@ void engine_exchange_cells(struct engine *e) {
sizeof(struct gpart) * s->size_gparts_foreign) != 0)
error("Failed to allocate foreign gpart data.");
}
memuse_report("gparts_foreign", sizeof(struct gpart) * s->size_gparts_foreign);
if (count_sparts_in > s->size_sparts_foreign) {
if (s->sparts_foreign != NULL) free(s->sparts_foreign);
s->size_sparts_foreign = 1.1 * count_sparts_in;
......@@ -1582,6 +1592,7 @@ void engine_exchange_cells(struct engine *e) {
sizeof(struct spart) * s->size_sparts_foreign) != 0)
error("Failed to allocate foreign spart data.");
}
memuse_report("sparts_foreign", sizeof(struct spart) * s->size_sparts_foreign);
/* Unpack the cells and link to the particle data. */
struct part *parts = s->parts_foreign;
......@@ -1793,6 +1804,9 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
}
}
}
memuse_report("parts", sizeof(struct part) * s->size_parts);
memuse_report("xparts", sizeof(struct xpart) * s->size_parts);
if (offset_sparts + count_sparts_in > s->size_sparts) {
message("re-allocating sparts array.");
s->size_sparts = (offset_sparts + count_sparts_in) * engine_parts_size_grow;
......@@ -1809,6 +1823,8 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
}
}
}
memuse_report("sparts", sizeof(struct spart) * s->size_sparts);
if (offset_gparts + count_gparts_in > s->size_gparts) {
message("re-allocating gparts array.");
s->size_gparts = (offset_gparts + count_gparts_in) * engine_parts_size_grow;
......@@ -1828,6 +1844,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
}
}
}
memuse_report("gparts", sizeof(struct gpart) * s->size_gparts);
/* Collect the requests for the particle data from the proxies. */
int nr_in = 0, nr_out = 0;
......@@ -4348,6 +4365,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
e->step = 0;
e->forcerebuild = 1;
e->wallclock_time = (float)clocks_diff(&time1, &time2);
memuse_report_str("step", memuse_process());
if (e->verbose) message("took %.3f %s.", e->wallclock_time, clocks_getunit());
}
......@@ -4391,6 +4409,7 @@ void engine_step(struct engine *e) {
e->max_active_bin = get_max_active_bin(e->ti_end_min);
e->min_active_bin = get_min_active_bin(e->ti_current, e->ti_old);
e->step += 1;
engine_cstep = e->step;
e->step_props = engine_step_prop_none;
if (e->policy & engine_policy_cosmology) {
......@@ -4532,6 +4551,8 @@ void engine_step(struct engine *e) {
/* Final job is to create a restart file if needed. */
engine_dump_restarts(e, drifted_all, e->restart_onexit && engine_is_done(e));
memuse_report_str("step", memuse_process());
}
/**
......@@ -5000,6 +5021,9 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
posix_memalign((void **)&xparts_new, xpart_align,
sizeof(struct xpart) * s->size_parts) != 0)
error("Failed to allocate new part data.");
memuse_report("parts", sizeof(struct part) * s->size_parts);
memuse_report("parts", sizeof(struct xpart) * s->size_parts);
if (s->nr_parts > 0) {
memcpy(parts_new, s->parts, sizeof(struct part) * s->nr_parts);
memcpy(xparts_new, s->xparts, sizeof(struct xpart) * s->nr_parts);
......@@ -5022,6 +5046,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
if (posix_memalign((void **)&sparts_new, spart_align,
sizeof(struct spart) * s->size_sparts) != 0)
error("Failed to allocate new spart data.");
memuse_report("sparts", sizeof(struct spart) * s->size_sparts);
if (s->nr_sparts > 0)
memcpy(sparts_new, s->sparts, sizeof(struct spart) * s->nr_sparts);
free(s->sparts);
......@@ -5040,6 +5066,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
if (posix_memalign((void **)&gparts_new, gpart_align,
sizeof(struct gpart) * s->size_gparts) != 0)
error("Failed to allocate new gpart data.");
memuse_report("gparts", sizeof(struct gpart) * s->size_gparts);
if (s->nr_gparts > 0)
memcpy(gparts_new, s->gparts, sizeof(struct gpart) * s->nr_gparts);
free(s->gparts);
......@@ -5683,6 +5711,8 @@ void engine_config(int restart, struct engine *e,
if (posix_memalign((void **)&e->runners, SWIFT_CACHE_ALIGNMENT,
e->nr_threads * sizeof(struct runner)) != 0)
error("Failed to allocate threads array.");
memuse_report("runners", e->nr_threads * sizeof(struct runner));
for (int k = 0; k < e->nr_threads; k++) {
e->runners[k].id = k;
e->runners[k].e = e;
......
......@@ -101,6 +101,11 @@ enum engine_step_properties {
*/
extern int engine_rank;
/**
* @brief The current step as a global variable (for messages).
*/
extern int engine_cstep;
/* Data structure for the engine. */
struct engine {
......
......@@ -36,12 +36,14 @@
/* Local headers. */
#include "clocks.h"
/* Use exit when not developing, avoids core dumps. */
#ifdef SWIFT_DEVELOP_MODE
#define swift_abort(errcode) abort()
#else
#define swift_abort(errcode) exit(errcode)
#endif
/**
* @brief Error macro. Prints the message given in argument and aborts.
*
......
/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
/**
* @file memuse.c
* @brief file of routines to report about memory use in SWIFT.
* Note reports are in KB.
*/
/* Config parameters. */
#include "../config.h"
/* Standard includes. */
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
/* Local includes. */
#include "clocks.h"
#include "engine.h"
/* Local macros to report output. */
#ifdef WITH_MPI
extern int engine_rank;
extern int engine_cstep;
#define memuse_output(what, memuse) \
({ \
printf("[%04i] %s :memuse: %i:%s %s\n", \
engine_rank, clocks_get_timesincestart(), engine_cstep, \
what, memuse); \
})
#else
#define memuse_output(what, memuse) \
({ \
printf("%s :memuse: %i:%s %s\n", \
clocks_get_timesincestart(), engine_cstep, what, memuse); \
})
#endif
/**
* @brief Report a memory allocation or use in bytes.
*
* @param what a name for the report, "parts", "gparts" etc.
* @param bytes the number of bytes that have been allocated
*/
void memuse_report(const char *what, size_t bytes) {
char buffer[32];
sprintf(buffer, "%zd", bytes/1024);
memuse_output(what, buffer);
}
/**
* @brief Report a memory allocation or use formatted description.
*
* @param what a name for the report, "parts", "gparts" etc.
* @param description the report, values should be in KB, the
* result of memuse_process() is suitable.
*/
void memuse_report_str(const char *what, const char *description) {
memuse_output(what, description);
}
/**
* @brief parse the process /proc/self/statm file to get the process
* memory use (in KB). Top field in ().
*
* @param size total virtual memory (VIRT)
* @param resident resident non-swapped memory (RES)
* @param share shared (mmap'd) memory (SHR)
* @param trs text (exe) resident set (CODE)
* @param lrs library resident set
* @param drs data+stack resident set (DATA)
* @param dt dirty pages (nDRT)
*/
void memuse_use(long *size, long *resident, long *share, long *trs,
long *lrs, long *drs, long *dt) {
/* Open the file. */
FILE *file = fopen("/proc/self/statm", "r");
if (file != NULL) {
int nscan = fscanf(file, "%ld %ld %ld %ld %ld %ld %ld", size, resident,
share, trs, lrs, drs, dt);
if (nscan == 7) {
/* Convert pages into bytes. Usually 4096, but could be 512 on some
* systems so take care in conversion to KB. */
long sz = sysconf(_SC_PAGESIZE);
*size *= sz;
*resident *= sz;
*share *= sz;
*trs *= sz;
*lrs *= sz;
*drs *= sz;
*dt *= sz;
*size /= 1024;
*resident /= 1024;
*share /= 1024;
*trs /= 1024;
*lrs /= 1024;
*drs /= 1024;
*dt /= 1024;
} else {
error("Failed to read sufficient fields from /proc/self/statm");
}
fclose(file);
} else {
error("Failed to open /proc/self/statm");
}
}
/**
* @brief Return a string with the current memory use of the process described.
*
* Not thread safe.
*
* @result the memory use of the process, note make a copy if not used
* immediately.
*/
char *memuse_process() {
static char buffer[256];
long size;
long resident;
long share;
long trs;
long lrs;
long drs;
long dt;
memuse_use(&size, &resident, &share, &trs, &lrs, &drs, &dt);
snprintf(buffer, 256, "VIRT = %ld SHR = %ld CODE = %ld DATA = %ld "
"RES = %ld", size, share, trs, drs, resident);
return buffer;
}
/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
#ifndef SWIFT_MEMUSE_H
#define SWIFT_MEMUSE_H
/* Config parameters. */
#include "../config.h"
void memuse_report(const char *what, size_t bytes);
void memuse_report_str(const char *what, const char *description);
void memuse_use(long *size, long *resident, long *share, long *trs,
long *lrs, long *drs, long *dt);
const char *memuse_process();
#endif /* SWIFT_MEMUSE_H */
......@@ -719,6 +719,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
(*Ngas) * sizeof(struct part)) != 0)
error("Error while allocating memory for particles");
bzero(*parts, *Ngas * sizeof(struct part));
memuse_report("parts", (*Ngas) * sizeof(struct part));
}
/* Allocate memory to store star particles */
......@@ -728,6 +729,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
*Nstars * sizeof(struct spart)) != 0)
error("Error while allocating memory for star particles");
bzero(*sparts, *Nstars * sizeof(struct spart));
memuse_report("sparts", (*Nstars) * sizeof(struct spart));
}
/* Allocate memory to store gravity particles */
......@@ -740,6 +742,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
*Ngparts * sizeof(struct gpart)) != 0)
error("Error while allocating memory for gravity particles");
bzero(*gparts, *Ngparts * sizeof(struct gpart));
memuse_report("gparts", (*Ngparts) * sizeof(struct gpart));
}
/* message("Allocated %8.2f MB for particles.", *N * sizeof(struct part) /
......@@ -1247,7 +1250,7 @@ void write_output_parallel(struct engine* e, const char* baseName,
case swift_type_dark_matter:
/* Allocate temporary array */
if (posix_memalign((void**)&dmparts, gpart_align,
if (swift_posix_memalign((void**)&dmparts, gpart_align,
Ndm * sizeof(struct gpart)) != 0)
error(
"Error while allocating temporart memory for "
......
......@@ -39,6 +39,7 @@
/* Local headers. */
#include "error.h"
#include "memuse.h"
/**
* @brief Exchange cells with a remote node.
......@@ -67,6 +68,8 @@ void proxy_cells_exch1(struct proxy *p) {
if (posix_memalign((void **)&p->pcells_out, SWIFT_STRUCT_ALIGNMENT,
sizeof(struct pcell) * p->size_pcells_out) != 0)
error("Failed to allocate pcell_out buffer.");
memuse_report("pcells_out", sizeof(struct pcell) * p->size_pcells_out);
for (int ind = 0, k = 0; k < p->nr_cells_out; k++) {
memcpy(&p->pcells_out[ind], p->cells_out[k]->pcell,
sizeof(struct pcell) * p->cells_out[k]->pcell_size);
......@@ -105,6 +108,7 @@ void proxy_cells_exch2(struct proxy *p) {
if (posix_memalign((void **)&p->pcells_in, SWIFT_STRUCT_ALIGNMENT,
sizeof(struct pcell) * p->size_pcells_in) != 0)
error("Failed to allocate pcell_in buffer.");
memuse_report("pcells_in", sizeof(struct pcell) * p->size_pcells_in);
/* Receive the particle buffers. */
int err = MPI_Irecv(p->pcells_in, sizeof(struct pcell) * p->size_pcells_in,
......
......@@ -53,6 +53,7 @@
#include "hydro.h"
#include "hydro_properties.h"
#include "kick.h"
#include "memuse.h"
#include "minmax.h"
#include "runner_doiact_fft.h"
#include "runner_doiact_vec.h"
......@@ -388,13 +389,16 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
#endif
/* start by allocating the entry arrays in the requested dimensions. */
size_t allocated = 0;
for (int j = 0; j < 13; j++) {
if ((flags & (1 << j)) && c->sort[j] == NULL) {
if ((c->sort[j] = (struct entry *)malloc(sizeof(struct entry) *
(count + 1))) == NULL)
error("Failed to allocate sort memory.");
allocated += sizeof(struct entry) * (count + 1);
}
}
memuse_report("c->sort[*]", allocated);
/* Does this cell have any progeny? */
if (c->split) {
......
......@@ -45,6 +45,7 @@
#include "error.h"
#include "intrinsics.h"
#include "kernel_hydro.h"
#include "memuse.h"
#include "queue.h"
#include "sort_part.h"
#include "space.h"
......@@ -1147,12 +1148,15 @@ void scheduler_reset(struct scheduler *s, int size) {
if (posix_memalign((void **)&s->tasks, task_align,
size * sizeof(struct task)) != 0)
error("Failed to allocate task array.");
memuse_report("tasks", size * sizeof(struct task));
if ((s->tasks_ind = (int *)malloc(sizeof(int) * size)) == NULL)
error("Failed to allocate task lists.");
memuse_report("tasks_ind", size * sizeof(int));
if ((s->tid_active = (int *)malloc(sizeof(int) * size)) == NULL)
error("Failed to allocate aactive task lists.");
memuse_report("tid_active", size * sizeof(int));
}
/* Reset the counters. */
......@@ -1770,6 +1774,7 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
if (posix_memalign((void **)&s->queues, queue_struct_align,
sizeof(struct queue) * nr_queues) != 0)
error("Failed to allocate queues.");
memuse_report("queues", sizeof(struct queue) * nr_queues);
/* Initialize each queue. */
for (int k = 0; k < nr_queues; k++) queue_init(&s->queues[k], NULL);
......
......@@ -48,6 +48,7 @@
#include "hydro_properties.h"
#include "io_properties.h"
#include "kernel_hydro.h"
#include "memuse.h"
#include "part.h"
#include "stars_io.h"
#include "units.h"
......@@ -573,6 +574,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
0)
error("Error while allocating memory for SPH particles");
bzero(*parts, *Ngas * sizeof(struct part));
memuse_report("parts", (*Ngas) * sizeof(struct part));
}
/* Allocate memory to store star particles */
......@@ -582,6 +584,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
*Nstars * sizeof(struct spart)) != 0)
error("Error while allocating memory for star particles");
bzero(*sparts, *Nstars * sizeof(struct spart));
memuse_report("sparts", (*Nstars) * sizeof(struct spart));
}
/* Allocate memory to store all gravity particles */
......@@ -594,6 +597,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
*Ngparts * sizeof(struct gpart)) != 0)
error("Error while allocating memory for gravity particles");
bzero(*gparts, *Ngparts * sizeof(struct gpart));
memuse_report("gparts", (*Ngparts) * sizeof(struct gpart));
}
/* message("Allocated %8.2f MB for particles.", *N * sizeof(struct part) / */
......
......@@ -47,6 +47,7 @@
#include "hydro_properties.h"
#include "io_properties.h"
#include "kernel_hydro.h"
#include "memuse.h"
#include "part.h"
#include "stars_io.h"
#include "units.h"
......@@ -465,6 +466,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units,
*Ngas * sizeof(struct part)) != 0)
error("Error while allocating memory for SPH particles");
bzero(*parts, *Ngas * sizeof(struct part));