1 /*
2 * Copyright (c) 2019,2020 NVIDIA CORPORATION.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <pwd.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24
25 #include "GB_jit_cache.h"
26
27 namespace jit {
28
29
30 // Get the directory in home to use for storing the cache
get_user_home_cache_dir()31 std::string get_user_home_cache_dir() {
32 auto home_dir = std::getenv("HOME");
33 if (home_dir != nullptr) {
34 return std::string(home_dir) + "/.GraphBLAS/";
35 } else {
36 return std::string();
37 }
38 }
39
40 // Default `GRAPHBLAS_CACHE_PATH` to `$HOME/.GraphBLAS`.
41 // This definition can be overridden at compile time by specifying a
42 // `-DGRAPHBLAS_CACHE_PATH=/kernel/cache/path` CMake argument.
43 // This path is used in the `getCacheDir()` function below.
44 #if !defined(GRAPHBLAS_CACHE_PATH)
45 #define GRAPHBLAS_CACHE_PATH get_user_home_cache_dir()
46 #endif
47
48 /**
49 * @brief Get the string path to the JITIFY kernel cache directory.
50 *
51 * This path can be overridden at runtime by defining an environment variable
52 * named `GRAPHBLAS_CACHE_PATH`. The value of this variable must be a path
53 * under which the process' user has read/write priveleges.
54 *
55 * This function returns a path to the cache directory, creating it if it
56 * doesn't exist.
57 *
58 * The default cache directory is `$HOME/.GraphBLAS`. If no overrides
59 * are used and if $HOME is not defined, returns an empty path and file
60 * caching is not used.
61 **/
getCacheDir()62 std::string getCacheDir() {
63 // The environment variable always overrides the
64 // default/compile-time value of `GRAPHBLAS_CACHE_PATH`
65 auto kernel_cache_path_env = std::getenv("GRAPHBLAS_CACHE_PATH");
66 auto kernel_cache_path = (kernel_cache_path_env != nullptr ? kernel_cache_path_env
67 : GRAPHBLAS_CACHE_PATH);
68
69 struct stat st;
70 if ( (stat( kernel_cache_path.c_str(), &st) != 0) ) {
71 // `mkdir -p` the kernel cache path if it doesn't exist
72 printf("cache is going to path %s\n", kernel_cache_path.c_str());
73 int status;
74 status = mkdir(kernel_cache_path.c_str(), 0777);
75 if (status != 0 ) return std::string();
76 //boost::filesystem::create_directories(kernel_cache_path);
77 }
78 return std::string(kernel_cache_path);
79 }
80
GBJitCache()81 GBJitCache::GBJitCache() { }
82
~GBJitCache()83 GBJitCache::~GBJitCache() { }
84
85 std::mutex GBJitCache::_kernel_cache_mutex;
86 std::mutex GBJitCache::_program_cache_mutex;
87
getProgram(std::string const & prog_name,std::string const & cuda_source,std::vector<std::string> const & given_headers,std::vector<std::string> const & given_options,jitify::experimental::file_callback_type file_callback)88 named_prog<jitify::experimental::Program> GBJitCache::getProgram(
89 std::string const& prog_name,
90 std::string const& cuda_source,
91 std::vector<std::string> const& given_headers,
92 std::vector<std::string> const& given_options,
93 jitify::experimental::file_callback_type file_callback)
94 {
95 // Lock for thread safety
96 std::lock_guard<std::mutex> lock(_program_cache_mutex);
97 //printf(" jit_cache get program %s\n", prog_name.c_str());
98
99 return getCached(prog_name, program_map,
100 [&](){
101 return jitify::experimental::Program(cuda_source,
102 given_headers,
103 given_options,
104 file_callback);
105 }
106 );
107 }
108
getKernelInstantiation(std::string const & kern_name,named_prog<jitify::experimental::Program> const & named_program,std::vector<std::string> const & arguments)109 named_prog<jitify::experimental::KernelInstantiation> GBJitCache::getKernelInstantiation(
110 std::string const& kern_name,
111 named_prog<jitify::experimental::Program> const& named_program,
112 std::vector<std::string> const& arguments)
113 {
114 // Lock for thread safety
115 std::lock_guard<std::mutex> lock(_kernel_cache_mutex);
116
117 std::string prog_name = std::get<0>(named_program);
118 jitify::experimental::Program& program = *std::get<1>(named_program);
119
120 // Make instance name e.g. "prog_binop.kernel_v_v_int_int_long int_Add"
121 std::string kern_inst_name = prog_name + '.' + kern_name;
122 for ( auto&& arg : arguments ) kern_inst_name += '_' + arg;
123
124 //printf(" got kernel instance %s\n",kern_inst_name.c_str());
125
126 return getCached(kern_inst_name, kernel_inst_map,
127 [&](){return program.kernel(kern_name)
128 .instantiate(arguments);
129 }
130 );
131 }
132
133 // Another overload for getKernelInstantiation which might be useful to get
134 // kernel instantiations in one step
135 // ------------------------------------------------------------------------
136 /*
137 jitify::experimental::KernelInstantiation GBJitCache::getKernelInstantiation(
138 std::string const& kern_name,
139 std::string const& prog_name,
140 std::string const& cuda_source = "",
141 std::vector<std::string> const& given_headers = {},
142 std::vector<std::string> const& given_options = {},
143 file_callback_type file_callback = nullptr)
144 {
145 auto program = getProgram(prog_name,
146 cuda_source,
147 given_headers,
148 given_options,
149 file_callback);
150 return getKernelInstantiation(kern_name, program);
151 }
152 */
153
cacheFile(std::string file_name)154 GBJitCache::cacheFile::cacheFile(std::string file_name)
155 : _file_name{file_name}
156 { }
157
~cacheFile()158 GBJitCache::cacheFile::~cacheFile() { }
159
read()160 std::string GBJitCache::cacheFile::read()
161 {
162 // Open file (duh)
163 int fd = open ( _file_name.c_str(), O_RDWR );
164 if ( fd == -1 ) {
165 // TODO: connect errors to GrB_error result
166 //printf(" failed to open cache file %s\n",_file_name.c_str());
167 successful_read = false;
168 return std::string();
169 }
170
171 // Lock the file descriptor. we the only ones now
172 if ( lockf(fd, F_LOCK, 0) == -1 ) {
173 successful_read = false;
174 return std::string();
175 }
176
177 // Get file descriptor from file pointer
178 FILE *fp = fdopen( fd, "rb" );
179
180 // Get file length
181 fseek( fp , 0L , SEEK_END);
182 size_t file_size = ftell( fp );
183 rewind( fp );
184
185 // Allocate memory of file length size
186 std::string content;
187 content.resize(file_size);
188 char *buffer = &content[0];
189
190 // Copy file into buffer
191 if( fread(buffer, file_size, 1, fp) != 1 ) {
192 //printf(" failed to read cache file %s\n",_file_name.c_str());
193 successful_read = false;
194 fclose(fp);
195 free(buffer);
196 return std::string();
197 }
198 fclose(fp);
199 successful_read = true;
200 printf(" read cache file %s\n",_file_name.c_str());
201
202 return content;
203 }
204
write(std::string content)205 void GBJitCache::cacheFile::write(std::string content)
206 {
207 // Open file and create if it doesn't exist, with access 0600
208 int fd = open ( _file_name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
209 if ( fd == -1 ) {
210 printf(" failed to open cache file for write %s\n",_file_name.c_str());
211 successful_write = false;
212 return;
213 }
214
215 // Lock the file descriptor. we the only ones now
216 if ( lockf(fd, F_LOCK, 0) == -1 ) {
217 successful_write = false;
218 return;
219 }
220
221 // Get file descriptor from file pointer
222 FILE *fp = fdopen( fd, "wb" );
223
224 // Copy string into file
225 if( fwrite(content.c_str(), content.length(), 1, fp) != 1 ) {
226 printf(" failed to write cache file %s\n",_file_name.c_str());
227 successful_write = false;
228 fclose(fp);
229 return;
230 }
231 fclose(fp);
232
233 successful_write = true;
234 //printf(" wrote cache file %s\n",_file_name.c_str());
235
236 return;
237 }
238
239 } // namespace jit
240