view scripts/miscellaneous/memory.m @ 30379:363fb10055df stable

maint: Style check m-files ahead of 7.1 release. * Map.m, integral3.m, logspace.m, quad2d.m, quadgk.m, quadl.m, tsearchn.m, get_first_help_sentence.m, print_usage.m, getframe.m, imformats.m, javaclasspath.m, condest.m, null.m, ordeig.m, inputParser.m, license.m, memory.m, methods.m, __publish_html_output__.m, __publish_latex_output__.m, publish.m, ode15s.m, fminbnd.m, fzero.m, configure_make.m, get_description.m, get_forge_pkg.m, annotation.m, camlookat.m, legend.m, __gnuplot_legend__.m, bar.m, colorbar.m, fill3.m, isosurface.m, plotyy.m, polar.m, __bar__.m, __ezplot__.m, __patch__.m, __pie__.m, __plt__.m, __scatter__.m, smooth3.m, stemleaf.m, __gnuplot_drawnow__.m, print.m, printd.m, __add_default_menu__.m, __gnuplot_draw_axes__.m, __gnuplot_print__.m, __print_parse_opts__.m, struct2hdl.m, profexport.m, profile.m, movfun.m, sprandsym.m, betaincinv.m, factor.m, nchoosek.m, gallery.m, hadamard.m, iqr.m, ranks.m, __run_test_suite__.m, test.m, datevec.m, weboptions.m: Style check m-files ahead of 7.1 release.
author Rik <rik@octave.org>
date Fri, 26 Nov 2021 20:53:22 -0800
parents 59c5131d46af
children 796f54d4ddbf
line wrap: on
line source

########################################################################
##
## Copyright (C) 2020-2021 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## This file is part of Octave.
##
## Octave is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Octave 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 General Public License
## along with Octave; see the file COPYING.  If not, see
## <https://www.gnu.org/licenses/>.
##
########################################################################

## -*- texinfo -*-
## @deftypefn  {} {} memory ()
## @deftypefnx {} {[@var{userdata}, @var{systemdata}] =} memory ()
## Display or return information about the memory usage of Octave.
##
## If the function is called without output arguments, a table with an overview
## of the current memory consumption is displayed.
##
## The output argument @var{userdata} is a structure with the following fields
## containing data for the Octave process:
##
## @table @code
## @item @var{MaxPossibleArrayBytes}
## Maximum size for an array to be allocated.  Be aware that this includes
## @emph{all} physical memory and swap space.  Allocating that amount of memory
## might result in system instability, data corruption, and/or file system
## corruption.  Note that depending on the platform (32-bit systems), the
## largest contiguous memory block might further limit the maximum possible
## allocatable array.  This check is not currently implemented.
##
## @item @var{MemAvailableAllArrays}
## The total size of available memory in bytes.
##
## @item @var{ram_available_all_arrays}
## The maximum size for an array that can be allocated in physical memory
## (excluding swap space).  Note that depending on the platform (32-bit
## systems), the largest contiguous memory block might further limit the
## maximum possible allocatable array.  This check is not currently
## implemented.
##
## @item  @var{MemUsedMATLAB}
## @itemx @var{mem_used_octave}
## The memory (including swap space) currently used by Octave in bytes.
##
## @item @var{ram_used_octave}
## The physical memory (excluding swap space) currently used by Octave in
## bytes.
##
## @end table
##
## The output argument @var{systemdata} is a nested structure with the
## following fields containing information about the system's memory:
##
## @table @code
## @item @var{PhysicalMemory.Available}
## The currently available physical memory in bytes.
##
## @item @var{PhysicalMemory.Total}
## The total physical memory in bytes.
##
## @item @var{SystemMemory.Available}
## The currently available memory (including swap space) in bytes.
##
## @item @var{SystemMemory.Total}
## The total memory (including swap space) in bytes.
##
## @item @var{VirtualAddressSpace.Available}
## The currently available virtual address space in bytes.
##
## @item @var{VirtualAddressSpace.Total}
## The total virtual address space in bytes.
##
## @end table
##
## @example
## @group
## memory ()
##    @result{} System    RAM: 3934008 KiB,  swap: 4087804 KiB
##       Octave    RAM:  170596 KiB,  virt: 1347944 KiB
##       Free      RAM: 1954940 KiB,  swap: 4087804 KiB
##       Available RAM: 2451948 KiB, total: 6042744 KiB
## @end group
##
## @group
## [userdata, systemdata] = memory ()
##    @result{} userdata =
##      scalar structure containing the fields:
##      MaxPossibleArrayBytes = 6.1622e+09
##      MemAvailableAllArrays = 6.1622e+09
##      ram_available_all_arrays = 2.4883e+09
##      MemUsedMATLAB = 1.3825e+09
##      mem_used_octave = 1.3825e+09
##      ram_used_octave = 1.7824e+08
##
##    systemdata =
##      scalar structure containing the fields:
##      PhysicalMemory =
##        scalar structure containing the fields:
##          Available = 2.4954e+09
##          Total = 4.0284e+09
##      SystemMemory =
##        scalar structure containing the fields:
##          Available = 6.6813e+09
##          Total = 8.2143e+09
##      VirtualAddressSpace =
##        scalar structure containing the fields:
##          Available = 2.8147e+14
##          Total = 2.8147e+14
## @end group
## @end example
##
## This function is implemented for Linux and Windows only.
##
## @seealso{computer, getpid, getrusage, nproc, uname}
## @end deftypefn

function [userdata, systemdata] = memory ()

  if ((! isunix () || ismac ()) && ! ispc ())
    if (nargout > 0)
      error ("memory: function not yet implemented for this architecture");
    else
      warning ("memory: function not yet implemented for this architecture");
    endif
    return;
  endif

  kiB = 1024;
  [architecture, bits] = computer ();

  if (isunix () && ! ismac ())
    ## Read values from pseudofiles
    [status, meminfo] = lmemory ();

    ## FIXME: Query the actual size of the user address space,
    ##        e.g., with getrlimit (RLIMIT_AS, rlp)
    if (log2 (bits) > 32)
      ## 64-bit platform
      address_space = 2^48;  # 256 TiB
    else
      ## 32-bit platform
      address_space = 3 * 2^30;  # 3 GiB
    endif

    total_ram = meminfo.MemTotal * kiB;
    total_swap = meminfo.SwapTotal * kiB;
    free_ram = meminfo.MemFree * kiB;
    if (isfield (meminfo, "MemAvailable"))
      available_ram = meminfo.MemAvailable * kiB;
    else
      ## On kernels from before 2014 MemAvailable is not present.
      ## This is a rough estimate that can be used instead.
      available_ram = (meminfo.MemFree + meminfo.Cached) * kiB;
    endif
    free_swap = meminfo.SwapFree * kiB;
    used_ram = status.VmRSS * kiB;
    used_virtual = status.VmSize * kiB;
    avail_virtual = address_space - used_virtual;

  elseif (ispc ())
    [proc, sys] = __wmemory__ ();

    total_ram = sys.TotalPhys;
    total_swap = sys.TotalPageFile;
    available_ram = sys.AvailPhys;
    free_swap = sys.AvailPageFile;
    used_ram = proc.WorkingSetSize;
    used_virtual = proc.WorkingSetSize + proc.PagefileUsage;
    avail_virtual = sys.AvailVirtual;
    address_space = sys.TotalVirtual;

  endif

  available = min (available_ram + free_swap, avail_virtual);
  ram_available = min (available_ram, avail_virtual);

  ## FIXME: On 32-bit systems, the largest possible array is limited by the
  ##        largest contiguous block in memory.
  user.MaxPossibleArrayBytes = available;
  user.MemAvailableAllArrays = available;
  user.ram_available_all_arrays = ram_available;
  user.MemUsedMATLAB = used_virtual;  # For compatibility
  user.mem_used_octave = used_virtual;
  user.ram_used_octave = used_ram;

  syst.PhysicalMemory.Available = available_ram;
  syst.PhysicalMemory.Total = total_ram;
  syst.SystemMemory.Available = available_ram + free_swap;
  syst.SystemMemory.Total = total_ram + total_swap;
  syst.VirtualAddressSpace.Available = avail_virtual;
  syst.VirtualAddressSpace.Total = address_space;

  if (nargout)
    userdata = user;
    systemdata = syst;
  else
    unitsize = kiB;
    unitname = 'kiB';
    disp (sprintf ("Octave is running on %s", architecture))
    disp (sprintf ("System    RAM: %9.0f %s,  swap: %9.0f %s",
                   round (syst.PhysicalMemory.Total / unitsize), unitname,
                   round (total_swap / unitsize), unitname ))
    disp (sprintf ("Octave    RAM: %9.0f %s,  virt: %9.0f %s",
                   round (user.ram_used_octave / unitsize), unitname,
                   round (user.mem_used_octave / unitsize), unitname))
    if (isunix ())
      ## The concept of free vs. available RAM doesn't seem to exist on Windows
      disp (sprintf ("Free      RAM: %9.0f %s,  swap: %9.0f %s",
                     round (free_ram / unitsize), unitname,
                     round (free_swap / unitsize), unitname))
    endif
    disp (sprintf ("Available RAM: %9.0f %s, total: %9.0f %s",
                   round (user.ram_available_all_arrays / unitsize), unitname,
                   round (user.MemAvailableAllArrays / unitsize), unitname))
  endif

endfunction

function [status, meminfo] = lmemory ()

  ## Read pseudo files to gather memory information on Linux

  ## Read the proc/self/status pseudofile.
  ## See https://linuxwiki.de/proc/pid#A.2Fproc.2Fpid.2Fstatus.
  ## It contains a variable number of lines with name-value pairs.

  f = fopen ("/proc/self/status");
  buffer = textscan (f, "%s %s", "delimiter", ':\n');
  fclose (f);
  for i = 1:rows (buffer{1})
    status.(buffer{1}{i}) = textscan (buffer{2}{i}){1};
  endfor

  ## Read the /proc/meminfo pseudofile
  ## see https://linuxwiki.de/proc/meminfo
  ## It contains a variable number of lines with name-value pairs.

  f = fopen ("/proc/meminfo");
  buffer = textscan (f, "%s %s", "delimiter", ':\n');
  fclose (f);
  for i = 1:rows (buffer{1})
    meminfo.(buffer{1}{i}) = textscan (buffer{2}{i}){1};
  endfor

endfunction


%!testif ; (isunix () && ! ismac ()) || ispc ()
%! [user, syst] = memory ();
%! assert (user.mem_used_octave > 0);
%! assert (user.ram_used_octave <= user.mem_used_octave);
%! assert (user.mem_used_octave < syst.SystemMemory.Total);
%! assert (user.MemAvailableAllArrays <= syst.SystemMemory.Available);

%!testif ; (! isunix () || ismac ()) && ! ispc ()
%! fail ("[user] = memory ()",
%!       "function not yet implemented for this architecture");
%! fail ("memory ()", "warning",
%!       "function not yet implemented for this architecture");