xref: /petsc/config/BuildSystem/config/utilities/cacheDetails.py (revision 3cca18fc1526ce0cac20e967ea546a5c32665257)
1import config.base
2import os
3import sys
4import collections
5
6class CacheAttribute(object):
7  def __init__(self, name, type_name, help_descr, default=None, min_value=0, max_value=min(sys.maxsize,2**31-1)):
8    self.name      = str(name)
9    self.type_name = str(type_name)
10    self.help      = str(help_descr)
11    self.default   = default
12    self.min       = int(min_value)
13    self.max       = int(max_value)
14
15  def valid(self,val):
16    return self.min <= val <= self.max
17
18  def enum(self):
19    return self.name.upper().replace('-','_')
20
21class Configure(config.base.Configure):
22  def __init__(self, framework):
23    config.base.Configure.__init__(self, framework)
24    self.headerPrefix = 'PETSC'
25    self.substPrefix  = 'PETSC'
26    self.updated      = 0
27    self.strmsg       = ''
28    self.attrs        = collections.OrderedDict(
29      level1_dcache_linesize=CacheAttribute(
30        'level1-dcache-linesize','int','Size in bytes of each line of the Level 1 data cache',
31        default=32,min_value=16
32      )
33    )
34# the next two are not currently used
35#                         CacheAttribute('level1-dcache-size', 'int', 'Size in bytes of Level 1 data cache', 32768, 16),
36#                         CacheAttribute('level1-dcache-assoc', 'int', 'Associativity of the Level 1 data cache, 0 for full associative', 2, 0)]
37    return
38
39  def __str__(self):
40    return self.strmsg
41
42  def setupHelp(self, help):
43    import nargs
44    for a in self.attrs.values():
45      help.addArgument('PETSc', '-known-'+a.name+'=<'+a.type_name+'>', nargs.ArgInt(None, None, a.help, min=a.min, max=a.max))
46    return
47
48  def setupDependencies(self, framework):
49    config.base.Configure.setupDependencies(self, framework)
50    self.compilers    = framework.require('config.compilers', self)
51    self.setCompilers = framework.require('config.setCompilers', self)
52    self.headers      = framework.require('config.headers', self)
53    return
54
55  def L1CacheLineSizeMethods(self,default_val):
56    var       = 'level1_dcache_linesize'
57    func_name = 'getconf_{}'.format(var)
58    if self.setCompilers.isDarwin(self.log):
59      yield (
60        func_name,
61        '\n'.join([
62          '#include <sys/sysctl.h>',
63          'int64_t {}(void) {{'.format(func_name),
64          '  int64_t linesize = {};'.format(default_val),
65          '  size_t  size = sizeof(linesize);',
66          '  int     ret = sysctlbyname("hw.cachelinesize", &linesize, &size, NULL, 0);',
67          '  return  ret ? {} : linesize;'.format(default_val),
68          '}'
69        ])
70      )
71    # On some systems, maybe just with glibc, sysconf can provide this stuff
72    yield (
73      func_name,
74      '\n'.join([
75        '#include <unistd.h>',
76        'long {}(void) {{'.format(func_name),
77        '  long val = sysconf(_SC_{});'.format(var.upper()),
78        '  return val >= 0 ? val : {};'.format(default_val),
79        '}'
80      ])
81    )
82    # A total hack since this will compile with any C compiler, but only return useful
83    # results when the getconf program is available
84    yield (
85      func_name,
86      '\n'.join([
87        '#include <stdio.h>',
88        'long {}(void) {{'.format(func_name),
89        '  long val = -1;',
90        '  FILE *f  = popen("getconf {}", "r");'.format(var.lower()),
91        '  fscanf(f, "%ld", &val);',
92        '  pclose(f);',
93        '  return val >= 0 ? val : {};'.format(default_val),
94        '}'
95      ])
96    )
97    return
98
99  def discoverL1CacheLineSize(self,attr):
100    """
101    Try to determine the L1CacheLineSize dynamically, if not possible returns the default value
102    """
103    filename       = 'conftestval'
104    main_body_base = '\n'.join([
105      '  FILE *output = fopen("{}", "w");'.format(filename),
106      '  if (!output) return 1;',
107      # note the '{func_name}', this is filled out below
108      '  fprintf(output, "%ld", (long){func_name}());',
109      '  fclose(output);'
110    ])
111    with self.Language('C'):
112      for func_name,includes in self.L1CacheLineSizeMethods(attr.default):
113        if not self.checkCompile(includes=includes,body=func_name+'();'):
114          continue
115
116        main_includes = '\n'.join(['#include <stdio.h>',includes])
117        main_body     = main_body_base.format(func_name=func_name)
118        if self.checkRun(includes=main_includes,body=main_body) and os.path.exists(filename):
119          with open(filename,"r") as f:
120            val = int(f.read())
121          os.remove(filename)
122          if attr.valid(val):
123            return val
124          self.log.write('Cannot use value returned for {}: {}, continuing\n'.format(attr.enum(),val))
125    return attr.default
126
127  def configureL1CacheLineSize(self):
128    """
129    Try to determine the size (in bytes) of an L1 cacheline. On success defines the
130    variable PETSC_LEVEL1_DCACHE_LINESIZE to the determined value.
131    """
132    attr        = self.attrs['level1_dcache_linesize']
133    argdb_val   = self.argDB.get('known-'+attr.name)
134    if argdb_val is not None:
135      val = int(argdb_val)
136    elif self.argDB['with-batch']:
137      self.log.write('Skipping determination of {} in batch mode, using default {}\n'.format(attr.enum(),attr.default))
138      val = attr.default
139    else:
140      val = self.discoverL1CacheLineSize(attr)
141    self.addDefine(attr.enum(),val)
142    return
143
144  def configure(self):
145    self.executeTest(self.configureL1CacheLineSize)
146    return
147