xobjects 0.5.9__tar.gz → 0.5.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xobjects-0.5.10/Architecture.md +202 -0
- xobjects-0.5.10/MANIFEST.in +9 -0
- {xobjects-0.5.9/xobjects.egg-info → xobjects-0.5.10}/PKG-INFO +1 -1
- xobjects-0.5.10/xobjects/_version.py +1 -0
- xobjects-0.5.10/xobjects/headers/atomicadd.h +350 -0
- xobjects-0.5.10/xobjects/headers/common.h +107 -0
- {xobjects-0.5.9 → xobjects-0.5.10/xobjects.egg-info}/PKG-INFO +1 -1
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects.egg-info/SOURCES.txt +6 -1
- xobjects-0.5.9/xobjects/_version.py +0 -1
- {xobjects-0.5.9 → xobjects-0.5.10}/LICENSE +0 -0
- /xobjects-0.5.9/README.md → /xobjects-0.5.10/Readme.md +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/pyproject.toml +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/setup.cfg +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/setup.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_align.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_array.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_buffer.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_capi.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_chunk.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_common.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_context_opencl.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_hybrid_class.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_kernel.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_linked_array.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_nplike_arrays.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_ref.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_scalars.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_shared_memory.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_strides.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_string.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_struct.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_to_dict.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_typeutils.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/tests/test_unionref.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/__init__.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/_patch_pyopencl_array.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/array.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/capi.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/context.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/context_cpu.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/context_cupy.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/context_pyopencl.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/general.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/hybrid_class.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/linkedarray.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/ref.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/scalar.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/specialize_source.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/string.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/struct.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/test_helpers.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/typeutils.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects/union.py +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects.egg-info/dependency_links.txt +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects.egg-info/entry_points.txt +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects.egg-info/requires.txt +0 -0
- {xobjects-0.5.9 → xobjects-0.5.10}/xobjects.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
The library is based on:
|
|
6
|
+
- Buffers: manage chunck of bytes on CPU and GPU (Cuda and OpenCL) memory using different backends
|
|
7
|
+
- Contexts: keep track of buffers, launch functions operating on buffers
|
|
8
|
+
- Basic types: a set of basic types that allow to describe structured data. Basic types are scalars, strings, structs, arrays and unions. User defined classes, composed with basic types, allow to interact with data on buffers and generate C functions to operate with data.
|
|
9
|
+
- C-API generation: The type information allows to write C functions that can be interact with data. The C functions are specialized for CPU, OpenCL and Cuda. Also loops described by annotation in comments are automatically generated for CPU, OpenCL and Cuda.
|
|
10
|
+
|
|
11
|
+
When a user defined class is created, Python methods and additional metadeta is attached to the class.
|
|
12
|
+
When an instance of a user defined class is instantiated, memory is written to a buffer and a Python object is created to interact with the data.
|
|
13
|
+
The size of an instance may not be known at class defintion time, but it cannot be changed after initialization.
|
|
14
|
+
|
|
15
|
+
## Contexts and Buffers
|
|
16
|
+
|
|
17
|
+
A context contain:
|
|
18
|
+
|
|
19
|
+
- `new_buffer(capacity)`: create new buffer of initial capacity
|
|
20
|
+
- `buffers`: list of weakref to buffers
|
|
21
|
+
- kernels : Object containing compiled code from sources/
|
|
22
|
+
|
|
23
|
+
Buffer:
|
|
24
|
+
- `buffer`: low level buffer
|
|
25
|
+
- `allocate(self,size)`: return the offset of the first free memory chunk of given size and grows the buffer if necessary
|
|
26
|
+
- `free(self,offset,size)`: free a chuck of memory of a given size
|
|
27
|
+
- `grow(self,capacity)`: increase the size of a buffer
|
|
28
|
+
- `_new_buffer(capacity)`: creae lowlevel buffer
|
|
29
|
+
- `update_from_native(...)`: Copy data from native buffer into self.buffer starting from offset
|
|
30
|
+
- `update_from_buffer(...)`: Copy data from python buffer such as bytearray, bytes, memoryview, numpy array.data
|
|
31
|
+
- `update_from_nplike(...)`: Copy data from nplike matching dest_dtype
|
|
32
|
+
- `update_from_xbuffer(...)`: Copy data from any xbuffer, don't pass through cpu if possible
|
|
33
|
+
- `copy_to_native(...)`: Copy data from self.buffer into dest
|
|
34
|
+
- `to_native(...)`: Return native data with content at from offset and nbytes
|
|
35
|
+
- `to_nplike(...)`: Return a view in of an nplike
|
|
36
|
+
- `to_bytearray(...)`: Return a byte array: used in update_from_xbuffer
|
|
37
|
+
- `to_pointer_arg(...)`: Return data that can be used as argument in kernel
|
|
38
|
+
|
|
39
|
+
## Types
|
|
40
|
+
|
|
41
|
+
Types can be composed of:
|
|
42
|
+
- scalar: numbers, String
|
|
43
|
+
- compound: Struct, Array, Ref, UnionRef
|
|
44
|
+
|
|
45
|
+
### Scalars
|
|
46
|
+
- examples: Float64, Int64, ...
|
|
47
|
+
- create: Float64(3.14)
|
|
48
|
+
- memory layout
|
|
49
|
+
- data
|
|
50
|
+
|
|
51
|
+
### String:
|
|
52
|
+
- create: String(string_or_int)
|
|
53
|
+
- memory layout
|
|
54
|
+
- size
|
|
55
|
+
- data
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Struct
|
|
59
|
+
- example: struct MyStruct field1 field2 ...
|
|
60
|
+
- create: Struct(dict), Struct(**args)
|
|
61
|
+
- memory layout:
|
|
62
|
+
- [ instance size ]
|
|
63
|
+
- static-field1
|
|
64
|
+
- ..
|
|
65
|
+
- static-fieldn
|
|
66
|
+
- [ offset field 2 ]
|
|
67
|
+
- [ offset ... ]
|
|
68
|
+
- [ offset field n ]
|
|
69
|
+
- [ dynamic-field1 ]
|
|
70
|
+
- [ ... ]
|
|
71
|
+
- [ dynamic-fieldn ]
|
|
72
|
+
|
|
73
|
+
### Array:
|
|
74
|
+
- example: 'array' 'f64' d1 d2 d3 'C' ; 'array' 'i64' d1 ':' d3 'F' ;
|
|
75
|
+
- where:
|
|
76
|
+
- 'C'/'F' is C-contiguos or F-contiguous
|
|
77
|
+
- d1, d2, .. are the dimensions for each axis and ':' means not specified
|
|
78
|
+
- create: Array(d1,d2,...) or Array([...]) or Array(np-array)
|
|
79
|
+
- memory layout:
|
|
80
|
+
- [size] if not _is_static_shape or not _is_static_type
|
|
81
|
+
- [dims ... ] len(_dynamic_shape)
|
|
82
|
+
- [strides...] if nd>1 and dynamic shapes
|
|
83
|
+
- [offsets] if not _is_static_type
|
|
84
|
+
- data
|
|
85
|
+
|
|
86
|
+
### Union:
|
|
87
|
+
- create: Union(obj), Union( (typename,args) )
|
|
88
|
+
- memory layout
|
|
89
|
+
- typeid
|
|
90
|
+
- data
|
|
91
|
+
|
|
92
|
+
### UnionRef:
|
|
93
|
+
- create: UnionRef(obj), UnionRef( (typename,args) )
|
|
94
|
+
- memory layout
|
|
95
|
+
- typeid
|
|
96
|
+
- offset
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
### Ref:
|
|
100
|
+
- memory layout
|
|
101
|
+
- offset
|
|
102
|
+
|
|
103
|
+
## Implementation details
|
|
104
|
+
|
|
105
|
+
Assumptions:
|
|
106
|
+
- Python user API cannot change the internal structure of the object but only the content
|
|
107
|
+
- Python object caches the structure information to avoid round trips to access data
|
|
108
|
+
- C user API does not manipulate structures
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
Operation:
|
|
112
|
+
|
|
113
|
+
- _inspect_args(*args, **args) -> info: Info
|
|
114
|
+
- check value and calculate size, offsets and dimensions from arguments, recursively
|
|
115
|
+
- returns at least `size` for `_get_a_buffer` and `value` for `_to_buffer`
|
|
116
|
+
|
|
117
|
+
- _get_a_buffer(size, _context, _buffer, _offset):
|
|
118
|
+
- make sure a valid buffer and offset is created
|
|
119
|
+
|
|
120
|
+
- _to_buffer(buffer, offset, value, info, size?):
|
|
121
|
+
- set data on buffer with offset from pyhon value, using info and implicitely respecting size
|
|
122
|
+
- if value same class -> binary copy (passing from context if needed)
|
|
123
|
+
|
|
124
|
+
- _from_buffer(buffer, offset) -> value:
|
|
125
|
+
- create python object from data on buffer. Can be a native type (scalar or string) or another XObject (struct, array)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
- __init__(self,*args,_context=None, _buffer=None, _offset=None,*nargs)
|
|
129
|
+
- initialize object data on buffer from args and create python object:
|
|
130
|
+
- use _pre_init(*args,**nargs) -> return args, nargs standard initialization value
|
|
131
|
+
- check values and calculate size using `_inspect_args`, return info object containing size, offsets and values to be later used by _to_buffer
|
|
132
|
+
- allocate using `_get_a_buffer`
|
|
133
|
+
- write data using `_to_buffer`
|
|
134
|
+
- use `_post_init` to complete object
|
|
135
|
+
`
|
|
136
|
+
- _update(self, value, size=None):
|
|
137
|
+
- Optional update object content using value, in case respecting size for string
|
|
138
|
+
|
|
139
|
+
- __get__(field,instance) or __getitem__(self,index...)
|
|
140
|
+
- if return instance._cache[field.index] else #should implement item caching for struct and array
|
|
141
|
+
- else return _from_buffer
|
|
142
|
+
|
|
143
|
+
- __set__(..., value) or __setitem__(...,value)
|
|
144
|
+
- if hasattr(self.ftype._update) get object and update in place
|
|
145
|
+
else call _to_buffer
|
|
146
|
+
|
|
147
|
+
- json enconding
|
|
148
|
+
nested object can be initialized using a json enconding
|
|
149
|
+
d:dict -> type(**d)
|
|
150
|
+
t:tuple -> type(*t)
|
|
151
|
+
a:other -> type(a)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
Class content:
|
|
156
|
+
|
|
157
|
+
- _inspect_args(cls, *args, **nargs) -> size, offsets
|
|
158
|
+
compute size and sizes and offsets from arguments
|
|
159
|
+
|
|
160
|
+
- _to_buffer(cls, buffer, offset, value, offsets=None):
|
|
161
|
+
initialize object on buffer
|
|
162
|
+
|
|
163
|
+
- _from_buffer(cls, buffer,offset):
|
|
164
|
+
create python object from initlialized object on buffer,offset
|
|
165
|
+
|
|
166
|
+
- _size: object size
|
|
167
|
+
None if dynamic
|
|
168
|
+
|
|
169
|
+
Instance content:
|
|
170
|
+
|
|
171
|
+
- _get_size(self) -> size
|
|
172
|
+
|
|
173
|
+
## C-API
|
|
174
|
+
|
|
175
|
+
Access
|
|
176
|
+
- `<Class>_get<_attrs>(obj, <indexes>)` -> scalar or object pointer
|
|
177
|
+
- `<Class>_set<_attrs>(obj, <indexes>, scalar)`
|
|
178
|
+
|
|
179
|
+
e.g. Multipole_get_field_normal(3)
|
|
180
|
+
|
|
181
|
+
Kernel convention:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
<fun>(void **refs){
|
|
185
|
+
Type0 arg0 = (Type0) refs[0];
|
|
186
|
+
....
|
|
187
|
+
Type1 argN = (TypeN) refs[N];
|
|
188
|
+
...
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
``` <fun>(buffer1, offset, ..., offset3){
|
|
193
|
+
Type0 arg0 = (Type0) refs[offset0];
|
|
194
|
+
,,,
|
|
195
|
+
TypeN argN = (TypeN) refs[offsetN];
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## TODO
|
|
200
|
+
|
|
201
|
+
- add free methods to delete data from buffer
|
|
202
|
+
- implement SOA types
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.10"
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// copyright ################################# //
|
|
2
|
+
// This file is part of the Xtrack Package. //
|
|
3
|
+
// Copyright (c) CERN, 2025. //
|
|
4
|
+
// ########################################### //
|
|
5
|
+
|
|
6
|
+
#ifndef _ATOMICADD_H_
|
|
7
|
+
#define _ATOMICADD_H_
|
|
8
|
+
|
|
9
|
+
#include "xobjects/headers/common.h"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// Ensure no conflicting atomicAdd macro is defined earlier for CPU or OpenCL
|
|
13
|
+
#ifndef XO_CONTEXT_CUDA
|
|
14
|
+
#ifdef atomicAdd
|
|
15
|
+
#warning "Warning: atomicAdd macro already defined, undefining it."
|
|
16
|
+
#undef atomicAdd
|
|
17
|
+
#endif // atomicAdd
|
|
18
|
+
#endif // XO_CONTEXT_CUDA
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
// ################################# //
|
|
22
|
+
// ### CPU Contexts ### //
|
|
23
|
+
// ################################# //
|
|
24
|
+
|
|
25
|
+
#ifdef XO_CONTEXT_CPU
|
|
26
|
+
#include <stdint.h>
|
|
27
|
+
|
|
28
|
+
#ifdef XO_CONTEXT_CPU_OPENMP
|
|
29
|
+
#ifndef _OPENMP
|
|
30
|
+
#error "XO_CONTEXT_CPU_OPENMP set, but compiled without -fopenmp"
|
|
31
|
+
#endif
|
|
32
|
+
// OpenMP atomic capture gives us read+write atomically
|
|
33
|
+
#define OMP_ATOMIC_CAPTURE _Pragma("omp atomic capture")
|
|
34
|
+
#else
|
|
35
|
+
#define OMP_ATOMIC_CAPTURE // No OpenMP: non-atomic fallback
|
|
36
|
+
#endif // XO_CONTEXT_CPU_OPENMP
|
|
37
|
+
|
|
38
|
+
// Macro to define atomicAdd for different types, will be overloaded via _Generic.
|
|
39
|
+
#define DEF_ATOMIC_ADD(T, SUF) \
|
|
40
|
+
GPUFUN T atomicAdd_##SUF(GPUGLMEM T *addr, T val) { \
|
|
41
|
+
T old; \
|
|
42
|
+
const T inc = (T)val; \
|
|
43
|
+
OMP_ATOMIC_CAPTURE \
|
|
44
|
+
{ old = *addr; *addr = *addr + inc; } \
|
|
45
|
+
return old; \
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
DEF_ATOMIC_ADD(int8_t , i8)
|
|
49
|
+
DEF_ATOMIC_ADD(int16_t, i16)
|
|
50
|
+
DEF_ATOMIC_ADD(int32_t, i32)
|
|
51
|
+
DEF_ATOMIC_ADD(int64_t, i64)
|
|
52
|
+
DEF_ATOMIC_ADD(uint8_t , u8)
|
|
53
|
+
DEF_ATOMIC_ADD(uint16_t, u16)
|
|
54
|
+
DEF_ATOMIC_ADD(uint32_t, u32)
|
|
55
|
+
DEF_ATOMIC_ADD(uint64_t, u64)
|
|
56
|
+
DEF_ATOMIC_ADD(float , f32)
|
|
57
|
+
DEF_ATOMIC_ADD(double , f64)
|
|
58
|
+
|
|
59
|
+
// Using _Generic to select the right function based on type (since C11).
|
|
60
|
+
// See https://en.cppreference.com/w/c/language/generic.html
|
|
61
|
+
#define atomicAdd(addr, val) _Generic((addr), \
|
|
62
|
+
int8_t*: atomicAdd_i8, \
|
|
63
|
+
int16_t*: atomicAdd_i16, \
|
|
64
|
+
int32_t*: atomicAdd_i32, \
|
|
65
|
+
int64_t*: atomicAdd_i64, \
|
|
66
|
+
uint8_t*: atomicAdd_u8, \
|
|
67
|
+
uint16_t*: atomicAdd_u16, \
|
|
68
|
+
uint32_t*: atomicAdd_u32, \
|
|
69
|
+
uint64_t*: atomicAdd_u64, \
|
|
70
|
+
float*: atomicAdd_f32, \
|
|
71
|
+
double*: atomicAdd_f64 \
|
|
72
|
+
)(addr, (val))
|
|
73
|
+
#endif // XO_CONTEXT_CPU
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
// ################################# //
|
|
77
|
+
// ### GPU Contexts ### //
|
|
78
|
+
// ################################# //
|
|
79
|
+
|
|
80
|
+
/* -------------- Design notes for GPU atomics ---------------------------------
|
|
81
|
+
* We will go for a unified approach for CUDA and OpenCL, with macros where needed.
|
|
82
|
+
* We implement 8/16-bit atomic add by atomically CAS'ing the *containing*
|
|
83
|
+
* 32-bit word. Only the target byte/halfword lane is modified; the neighbor
|
|
84
|
+
* lane is preserved. This is linearisable: each successful CAS is one atomic
|
|
85
|
+
* RMW on the 32-bit word.
|
|
86
|
+
* Assumptions: little-endian lane layout (true on NVIDIA) and natural alignment
|
|
87
|
+
* of the 16-bit addresses (addr % 2 == 0). 8-bit has no alignment requirement.
|
|
88
|
+
* Return value matches CUDA semantics: the **old** value at *addr* (fetch-add).
|
|
89
|
+
* ---------------------------------------------------------------------------*/
|
|
90
|
+
|
|
91
|
+
// Info: a CAS function (Compare-And-Swap) takes three arguments: a pointer to
|
|
92
|
+
// the value to be modified, the expected old value, and the new value. The
|
|
93
|
+
// second argument helps to recognise if another thread has modified the value
|
|
94
|
+
// in the meantime. The function compares the value at the address with the
|
|
95
|
+
// expected values, and if they are the same, it writes the new value at the
|
|
96
|
+
// address. In any case, the function returns the actual old value at the
|
|
97
|
+
// address. If the returned value is different from the expected value, it
|
|
98
|
+
// means that another thread has modified the value in the meantime.
|
|
99
|
+
|
|
100
|
+
// Define types and macros for CUDA and OpenCL
|
|
101
|
+
// -------------------------------------------
|
|
102
|
+
#if defined(XO_CONTEXT_CUDA)
|
|
103
|
+
// CUDA compiler may not have <stdint.h>, so define the types if needed.
|
|
104
|
+
#ifdef __CUDACC_RTC__
|
|
105
|
+
// NVRTC (CuPy RawModule default) can’t see <stdint.h>, so detect it via __CUDACC_RTC__
|
|
106
|
+
typedef signed char int8_t;
|
|
107
|
+
typedef short int16_t;
|
|
108
|
+
typedef int int32_t;
|
|
109
|
+
typedef long long int64_t;
|
|
110
|
+
typedef unsigned char uint8_t;
|
|
111
|
+
typedef unsigned short uint16_t;
|
|
112
|
+
typedef unsigned int uint32_t;
|
|
113
|
+
typedef unsigned long long uint64_t;
|
|
114
|
+
#else
|
|
115
|
+
// Alternatively, NVCC path is fine with host headers
|
|
116
|
+
#include <stdint.h>
|
|
117
|
+
#endif // __CUDACC_RTC__
|
|
118
|
+
#define GPUVOLATILE GPUGLMEM
|
|
119
|
+
#define __XT_CAS_U32(ptr, exp, val) atomicCAS((GPUVOLATILE uint32_t*)(ptr), (uint32_t)(exp), (uint32_t)(val))
|
|
120
|
+
#define __XT_CAS_U64(ptr, exp, val) atomicCAS((GPUVOLATILE uint64_t*)(ptr), (uint64_t)(exp), (uint64_t)(val))
|
|
121
|
+
#define __XT_AS_U32_FROM_F32(x) __float_as_uint(x)
|
|
122
|
+
#define __XT_AS_F32_FROM_U32(x) __uint_as_float(x)
|
|
123
|
+
#define __XT_AS_U64_FROM_F64(x) __double_as_longlong(x)
|
|
124
|
+
#define __XT_AS_F64_FROM_U64(x) __longlong_as_double(x)
|
|
125
|
+
#elif defined(XO_CONTEXT_CL)
|
|
126
|
+
// It seems OpenCL already has the types from <stdint.h> defined.
|
|
127
|
+
// typedef char int8_t;
|
|
128
|
+
// typedef short int16_t;
|
|
129
|
+
// typedef int int32_t;
|
|
130
|
+
// typedef long int64_t;
|
|
131
|
+
// typedef unsigned char uint8_t;
|
|
132
|
+
// typedef unsigned short uint16_t;
|
|
133
|
+
// typedef unsigned int uint32_t;
|
|
134
|
+
// typedef unsigned long uint64_t;
|
|
135
|
+
#define GPUVOLATILE GPUGLMEM volatile
|
|
136
|
+
#if __OPENCL_C_VERSION__ < 110
|
|
137
|
+
// Map 1.0 "atom_*" names to 1.1+ "atomic_*"
|
|
138
|
+
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
|
|
139
|
+
#define atomic_add atom_add
|
|
140
|
+
#define atomic_cmpxchg atom_cmpxchg
|
|
141
|
+
#endif
|
|
142
|
+
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
|
|
143
|
+
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
|
|
144
|
+
#define __XT_CAS_U32(ptr, exp, val) atomic_cmpxchg((GPUVOLATILE uint32_t*)(ptr), (uint32_t)(exp), (uint32_t)(val))
|
|
145
|
+
#define __XT_CAS_U64(ptr, exp, val) atom_cmpxchg((GPUVOLATILE uint64_t*)(ptr), (uint64_t)(exp), (uint64_t)(val))
|
|
146
|
+
#define __XT_AS_U32_FROM_F32(x) as_uint(x)
|
|
147
|
+
#define __XT_AS_F32_FROM_U32(x) as_float(x)
|
|
148
|
+
#define __XT_AS_U64_FROM_F64(x) as_ulong(x)
|
|
149
|
+
#define __XT_AS_F64_FROM_U64(x) as_double(x)
|
|
150
|
+
#endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
|
|
151
|
+
|
|
152
|
+
// Define atomic functions per type
|
|
153
|
+
// --------------------------------
|
|
154
|
+
#if defined(XO_CONTEXT_CUDA) || defined(XO_CONTEXT_CL)
|
|
155
|
+
// Helper: compute (base 32-bit word pointer, shift, mask) for a byte in that word.
|
|
156
|
+
GPUFUN inline void __xt_lane8(GPUVOLATILE void* addr, GPUVOLATILE uint32_t** w, uint32_t* sh, uint32_t* mask){
|
|
157
|
+
size_t a = (size_t)addr;
|
|
158
|
+
*w = (GPUVOLATILE uint32_t*)(a & ~(size_t)3); // word: align down to 4-byte boundary
|
|
159
|
+
*sh = (uint32_t)((a & (size_t)3) * 8U); // shift 0,8,16,24 depending on byte lane
|
|
160
|
+
*mask = (uint32_t)(0xFFu << *sh);
|
|
161
|
+
}
|
|
162
|
+
// Helper: same for a halfword (16-bit) in the containing 32-bit word.
|
|
163
|
+
GPUFUN inline void __xt_lane16(GPUVOLATILE void* addr, GPUVOLATILE uint32_t** w, uint32_t* sh, uint32_t* mask){
|
|
164
|
+
size_t a = (size_t)addr;
|
|
165
|
+
*w = (GPUVOLATILE uint32_t*)(a & ~(size_t)3); // word: align down to 4-byte boundary
|
|
166
|
+
*sh = (uint32_t)((a & (size_t)2) ? 16U : 0U); // shift0 or 16 depending on halfword
|
|
167
|
+
*mask = (uint32_t)(0xFFFFU << *sh);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ---------------- 8-bit: int8_t / uint8_t (CAS on 32-bit word) --------------
|
|
171
|
+
GPUFUN int8_t atomicAdd_i8(GPUVOLATILE int8_t* addr, int8_t val){
|
|
172
|
+
GPUVOLATILE uint32_t *w;
|
|
173
|
+
uint32_t sh, mask;
|
|
174
|
+
__xt_lane8(addr, &w, &sh, &mask);
|
|
175
|
+
uint32_t old = *w, assumed, b, nb, nw; // byte, newbyte, newword
|
|
176
|
+
do {
|
|
177
|
+
assumed = old;
|
|
178
|
+
b = (assumed & mask) >> sh; // Extract current 8-bit lane
|
|
179
|
+
nb = (uint32_t)((uint8_t)b + (uint8_t)val); // Add in modulo-256 (two's complement)
|
|
180
|
+
nw = (assumed & ~mask) | ((nb & 0xFFU) << sh); // Merge back updated lane; leave neighbor lanes intact
|
|
181
|
+
// Try to publish; if someone raced us, retry with their value
|
|
182
|
+
old = __XT_CAS_U32(w, assumed, nw);
|
|
183
|
+
} while (old != assumed);
|
|
184
|
+
return (int8_t)((assumed & mask) >> sh);
|
|
185
|
+
}
|
|
186
|
+
GPUFUN uint8_t atomicAdd_u8(GPUVOLATILE uint8_t* addr, uint8_t val){
|
|
187
|
+
GPUVOLATILE uint32_t *w;
|
|
188
|
+
uint32_t sh, mask;
|
|
189
|
+
__xt_lane8(addr, &w, &sh, &mask);
|
|
190
|
+
uint32_t old = *w, assumed, b, nb, nw;
|
|
191
|
+
do {
|
|
192
|
+
assumed = old;
|
|
193
|
+
b = (assumed & mask) >> sh;
|
|
194
|
+
nb = (uint32_t)(b + val); /* mod 256 */
|
|
195
|
+
nw = (assumed & ~mask) | ((nb & 0xFFU) << sh);
|
|
196
|
+
old = __XT_CAS_U32(w, assumed, nw);
|
|
197
|
+
} while (old != assumed);
|
|
198
|
+
return (uint8_t)((assumed & mask) >> sh);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---------------- 16-bit: int16_t / uint16_t (CAS on 32-bit word) -----------
|
|
202
|
+
GPUFUN int16_t atomicAdd_i16(GPUVOLATILE int16_t* addr, int16_t val){
|
|
203
|
+
GPUVOLATILE uint32_t* w;
|
|
204
|
+
uint32_t sh, mask;
|
|
205
|
+
__xt_lane16(addr, &w, &sh, &mask);
|
|
206
|
+
uint32_t old = *w, assumed, b, nb, nw;
|
|
207
|
+
do {
|
|
208
|
+
assumed = old;
|
|
209
|
+
b = (assumed & mask) >> sh;
|
|
210
|
+
nb = (uint32_t)((uint16_t)b + (uint16_t)val);
|
|
211
|
+
nw = (assumed & ~mask) | ((nb & 0xFFFFU) << sh);
|
|
212
|
+
old = __XT_CAS_U32(w, assumed, nw);
|
|
213
|
+
} while (old != assumed);
|
|
214
|
+
return (int16_t)((assumed & mask) >> sh);
|
|
215
|
+
}
|
|
216
|
+
GPUFUN uint16_t atomicAdd_u16(GPUVOLATILE uint16_t* addr, uint16_t val){
|
|
217
|
+
GPUVOLATILE uint32_t* w;
|
|
218
|
+
uint32_t sh, mask;
|
|
219
|
+
__xt_lane16(addr, &w, &sh, &mask);
|
|
220
|
+
uint32_t old = *w, assumed, b, nb, nw;
|
|
221
|
+
do {
|
|
222
|
+
assumed = old;
|
|
223
|
+
b = (assumed & mask) >> sh;
|
|
224
|
+
nb = (uint32_t)(b + val);
|
|
225
|
+
nw = (assumed & ~mask) | ((nb & 0xFFFFU) << sh);
|
|
226
|
+
old = __XT_CAS_U32(w, assumed, nw);
|
|
227
|
+
} while (old != assumed);
|
|
228
|
+
return (uint16_t)((assumed & mask) >> sh);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ---------------- 32-bit: int32_t / uint32_t (built-in) -----------
|
|
232
|
+
GPUFUN int32_t atomicAdd_i32(GPUVOLATILE int32_t* addr, int32_t val){
|
|
233
|
+
#ifdef XO_CONTEXT_CUDA
|
|
234
|
+
return atomicAdd(addr, val);
|
|
235
|
+
#else // XO_CONTEXT_CL
|
|
236
|
+
return atomic_add(addr, val);
|
|
237
|
+
#endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
|
|
238
|
+
}
|
|
239
|
+
GPUFUN uint32_t atomicAdd_u32(GPUVOLATILE uint32_t* addr, uint32_t val){
|
|
240
|
+
#ifdef XO_CONTEXT_CUDA
|
|
241
|
+
return atomicAdd(addr, val);
|
|
242
|
+
#else // XO_CONTEXT_CL
|
|
243
|
+
return atomic_add(addr, val);
|
|
244
|
+
#endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ---------------- 64-bit: int64_t / uint64_t (built-in or CAS on 64-bit word) -----------
|
|
248
|
+
GPUFUN int64_t atomicAdd_i64(GPUVOLATILE int64_t* addr, int64_t val){
|
|
249
|
+
uint64_t old, nw;
|
|
250
|
+
do {
|
|
251
|
+
old = *addr;
|
|
252
|
+
nw = old + val;
|
|
253
|
+
} while (__XT_CAS_U64((GPUVOLATILE uint64_t*)addr, old, nw) != old);
|
|
254
|
+
return old;
|
|
255
|
+
}
|
|
256
|
+
GPUFUN uint64_t atomicAdd_u64(GPUVOLATILE uint64_t* addr, uint64_t val){
|
|
257
|
+
#ifdef XO_CONTEXT_CUDA
|
|
258
|
+
return atomicAdd(addr, val);
|
|
259
|
+
#else // XO_CONTEXT_CL
|
|
260
|
+
return atom_add(addr, val);
|
|
261
|
+
#endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------- 32-bit: float (built-in or CAS on 32-bit word) -----------
|
|
265
|
+
GPUFUN float atomicAdd_f32(GPUVOLATILE float* addr, float val){
|
|
266
|
+
#ifdef XO_CONTEXT_CUDA
|
|
267
|
+
return atomicAdd(addr, val);
|
|
268
|
+
#else // XO_CONTEXT_CL
|
|
269
|
+
uint32_t old, nw;
|
|
270
|
+
do {
|
|
271
|
+
old = __XT_AS_U32_FROM_F32(*addr);
|
|
272
|
+
nw = __XT_AS_U32_FROM_F32(__XT_AS_F32_FROM_U32(old) + val);
|
|
273
|
+
} while (__XT_CAS_U32((GPUVOLATILE uint32_t*)addr, old, nw) != old);
|
|
274
|
+
return __XT_AS_F32_FROM_U32(old);
|
|
275
|
+
#endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ---------------- 64-bit: float (built-in or CAS on 64-bit word) -----------
|
|
279
|
+
GPUFUN double atomicAdd_f64(GPUVOLATILE double* addr, double val){
|
|
280
|
+
#if __CUDA_ARCH__ >= 600
|
|
281
|
+
return atomicAdd(addr, val);
|
|
282
|
+
#else // XO_CONTEXT_CL || __CUDA_ARCH__ < 600
|
|
283
|
+
uint64_t old, nw;
|
|
284
|
+
do {
|
|
285
|
+
old = __XT_AS_U64_FROM_F64(*addr);
|
|
286
|
+
nw = __XT_AS_U64_FROM_F64(__XT_AS_F64_FROM_U64(old) + val);
|
|
287
|
+
} while (__XT_CAS_U64((GPUVOLATILE uint64_t*)addr, old, nw) != old);
|
|
288
|
+
return __XT_AS_F64_FROM_U64(old);
|
|
289
|
+
#endif // __CUDA_ARCH__ >= 600 / XO_CONTEXT_CL
|
|
290
|
+
}
|
|
291
|
+
#endif // defined(XO_CONTEXT_CUDA) || defined(XO_CONTEXT_CL)
|
|
292
|
+
|
|
293
|
+
// Define the overloaded function
|
|
294
|
+
// ------------------------------
|
|
295
|
+
#ifdef XO_CONTEXT_CUDA
|
|
296
|
+
// NVRTC (CuPy RawModule) usually compiles under extern "C".
|
|
297
|
+
// In C, function overloading is not possible, but we can cheat by doing it in
|
|
298
|
+
// C++ (with a different name to avoid clashes with the built-in atomicAdd).
|
|
299
|
+
// This function will then be remapped to atomicAdd() via a macro in C.
|
|
300
|
+
#ifdef __cplusplus
|
|
301
|
+
extern "C++" {
|
|
302
|
+
|
|
303
|
+
GPUFUN int8_t xt_atomicAdd(GPUVOLATILE int8_t* p, int8_t v) { return atomicAdd_i8 (p, v); }
|
|
304
|
+
GPUFUN uint8_t xt_atomicAdd(GPUVOLATILE uint8_t* p, uint8_t v) { return atomicAdd_u8 (p, v); }
|
|
305
|
+
GPUFUN int16_t xt_atomicAdd(GPUVOLATILE int16_t* p, int16_t v) { return atomicAdd_i16(p, v); }
|
|
306
|
+
GPUFUN uint16_t xt_atomicAdd(GPUVOLATILE uint16_t*p, uint16_t v) { return atomicAdd_u16(p, v); }
|
|
307
|
+
GPUFUN int64_t xt_atomicAdd(GPUVOLATILE int64_t*p, int64_t v) { return atomicAdd_i64(p, v); }
|
|
308
|
+
|
|
309
|
+
// Existing type definitions: forward to CUDA built-ins
|
|
310
|
+
GPUFUN int32_t xt_atomicAdd(GPUVOLATILE int32_t* p, int32_t v) { return ::atomicAdd(p, v); }
|
|
311
|
+
GPUFUN uint32_t xt_atomicAdd(GPUVOLATILE uint32_t* p, uint32_t v) { return ::atomicAdd(p, v); }
|
|
312
|
+
GPUFUN uint64_t xt_atomicAdd(GPUVOLATILE uint64_t* p, uint64_t v) { return ::atomicAdd(p, v); }
|
|
313
|
+
GPUFUN float xt_atomicAdd(GPUVOLATILE float* p, float v) { return ::atomicAdd(p, v); }
|
|
314
|
+
#if __CUDA_ARCH__ >= 600
|
|
315
|
+
GPUFUN double xt_atomicAdd(GPUVOLATILE double* p, double v) { return ::atomicAdd(p, v); }
|
|
316
|
+
#else
|
|
317
|
+
GPUFUN double xt_atomicAdd(GPUVOLATILE double* p, double v) { return atomicAdd_f64(p, v); }
|
|
318
|
+
#endif
|
|
319
|
+
|
|
320
|
+
}
|
|
321
|
+
#endif // __cplusplus
|
|
322
|
+
|
|
323
|
+
// ---------- Global remap of the public name on device code ----------
|
|
324
|
+
// Define AFTER the wrappers so we don't macro-rewrite our own calls.
|
|
325
|
+
#ifdef atomicAdd
|
|
326
|
+
#undef atomicAdd
|
|
327
|
+
#endif
|
|
328
|
+
#define atomicAdd(ptr, val) xt_atomicAdd((ptr), (val))
|
|
329
|
+
#endif /* XO_CONTEXT_CUDA */
|
|
330
|
+
|
|
331
|
+
#ifdef XO_CONTEXT_CL
|
|
332
|
+
#if !__has_attribute(overloadable)
|
|
333
|
+
#error "The current OpenCL compiler/architecture does not support __attribute__((overloadable))"
|
|
334
|
+
#endif
|
|
335
|
+
#define OCL_OVERLOAD __attribute__((overloadable))
|
|
336
|
+
GPUFUN int8_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int8_t* p, int8_t v) { return atomicAdd_i8 (p, v); }
|
|
337
|
+
GPUFUN uint8_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint8_t* p, uint8_t v) { return atomicAdd_u8 (p, v); }
|
|
338
|
+
GPUFUN int16_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int16_t* p, int16_t v) { return atomicAdd_i16(p, v); }
|
|
339
|
+
GPUFUN uint16_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint16_t*p, uint16_t v) { return atomicAdd_u16(p, v); }
|
|
340
|
+
GPUFUN int64_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int64_t*p, int64_t v) { return atomicAdd_i64(p, v); }
|
|
341
|
+
GPUFUN float OCL_OVERLOAD atomicAdd(GPUVOLATILE float* p, float v) { return atomicAdd_f32(p, v); }
|
|
342
|
+
GPUFUN double OCL_OVERLOAD atomicAdd(GPUVOLATILE double* p, double v) { return atomicAdd_f64(p, v); }
|
|
343
|
+
|
|
344
|
+
// Existing type definitions: forward to OpenCL built-ins
|
|
345
|
+
GPUFUN int32_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int32_t* p, int32_t v) { return atomic_add(p, v); }
|
|
346
|
+
GPUFUN uint32_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint32_t* p, uint32_t v) { return atomic_add(p, v); }
|
|
347
|
+
GPUFUN uint64_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint64_t* p, uint64_t v) { return atom_add(p, v); }
|
|
348
|
+
#endif // XO_CONTEXT_CL
|
|
349
|
+
|
|
350
|
+
#endif //_ATOMICADD_H_
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// copyright ################################# //
|
|
2
|
+
// This file is part of the Xtrack Package. //
|
|
3
|
+
// Copyright (c) CERN, 2025. //
|
|
4
|
+
// ########################################### //
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#ifndef XOBJECTS_COMMON_H
|
|
8
|
+
#define XOBJECTS_COMMON_H
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
Common macros for vectorization and parallelization, as well as common
|
|
12
|
+
arithmetic operations.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
#ifdef XO_CONTEXT_CPU_SERIAL
|
|
16
|
+
// We are on CPU, without OpenMP
|
|
17
|
+
|
|
18
|
+
#define VECTORIZE_OVER(INDEX_NAME, COUNT) \
|
|
19
|
+
for (int64_t INDEX_NAME = 0; INDEX_NAME < (COUNT); INDEX_NAME++) {
|
|
20
|
+
|
|
21
|
+
#define END_VECTORIZE \
|
|
22
|
+
}
|
|
23
|
+
#endif // XO_CONTEXT_CPU_SERIAL
|
|
24
|
+
|
|
25
|
+
#ifdef XO_CONTEXT_CPU_OPENMP
|
|
26
|
+
// We are on CPU with the OpenMP context switched on
|
|
27
|
+
|
|
28
|
+
#define VECTORIZE_OVER(INDEX_NAME, COUNT) \
|
|
29
|
+
_Pragma("omp parallel for") \
|
|
30
|
+
for (int64_t INDEX_NAME = 0; INDEX_NAME < (COUNT); INDEX_NAME++) {
|
|
31
|
+
|
|
32
|
+
#define END_VECTORIZE \
|
|
33
|
+
}
|
|
34
|
+
#endif // XO_CONTEXT_CPU_OPENMP
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
#ifdef XO_CONTEXT_CUDA
|
|
38
|
+
// We are on a CUDA GPU
|
|
39
|
+
|
|
40
|
+
#define VECTORIZE_OVER(INDEX_NAME, COUNT) { \
|
|
41
|
+
int64_t INDEX_NAME = blockDim.x * blockIdx.x + threadIdx.x; \
|
|
42
|
+
if (INDEX_NAME < (COUNT)) {
|
|
43
|
+
|
|
44
|
+
#define END_VECTORIZE \
|
|
45
|
+
} \
|
|
46
|
+
}
|
|
47
|
+
#endif // XO_CONTEXT_CUDA
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
#ifdef XO_CONTEXT_CL
|
|
51
|
+
// We are on an OpenCL GPU
|
|
52
|
+
|
|
53
|
+
#define VECTORIZE_OVER(INDEX_NAME, COUNT) \
|
|
54
|
+
{ \
|
|
55
|
+
int64_t INDEX_NAME = get_global_id(0); \
|
|
56
|
+
if (INDEX_NAME < (COUNT)) { \
|
|
57
|
+
|
|
58
|
+
#define END_VECTORIZE \
|
|
59
|
+
} \
|
|
60
|
+
}
|
|
61
|
+
#endif // XO_CONTEXT_CL
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
Qualifier keywords for GPU and optimisation
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
#ifdef XO_CONTEXT_CPU // for both serial and OpenMP
|
|
69
|
+
#define GPUKERN
|
|
70
|
+
#define GPUFUN static inline
|
|
71
|
+
#define GPUGLMEM
|
|
72
|
+
#define RESTRICT restrict
|
|
73
|
+
#endif
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
#ifdef XO_CONTEXT_CUDA
|
|
77
|
+
#define GPUKERN __global__
|
|
78
|
+
#define GPUFUN __device__
|
|
79
|
+
#define GPUGLMEM
|
|
80
|
+
#define RESTRICT
|
|
81
|
+
#endif // XO_CONTEXT_CUDA
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
#ifdef XO_CONTEXT_CL
|
|
85
|
+
#define GPUKERN __kernel
|
|
86
|
+
#define GPUFUN
|
|
87
|
+
#define GPUGLMEM __global
|
|
88
|
+
#define RESTRICT
|
|
89
|
+
#endif // XO_CONTEXT_CL
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/*
|
|
93
|
+
Common maths-related macros
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
#define POW2(X) ((X)*(X))
|
|
97
|
+
#define POW3(X) ((X)*(X)*(X))
|
|
98
|
+
#define POW4(X) ((X)*(X)*(X)*(X))
|
|
99
|
+
#define NONZERO(X) ((X) != 0.0)
|
|
100
|
+
#define NONZERO_TOL(X, TOL) (fabs((X)) > (TOL))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
#ifndef VECTORIZE_OVER
|
|
104
|
+
#error "Unknown context, or the expected context (XO_CONTEXT_*) flag undefined. Try updating Xobjects?"
|
|
105
|
+
#endif
|
|
106
|
+
|
|
107
|
+
#endif // XOBJECTS_COMMON_H
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
Architecture.md
|
|
1
2
|
LICENSE
|
|
3
|
+
MANIFEST.in
|
|
2
4
|
README.md
|
|
5
|
+
Readme.md
|
|
3
6
|
pyproject.toml
|
|
4
7
|
setup.cfg
|
|
5
8
|
setup.py
|
|
@@ -48,4 +51,6 @@ xobjects.egg-info/SOURCES.txt
|
|
|
48
51
|
xobjects.egg-info/dependency_links.txt
|
|
49
52
|
xobjects.egg-info/entry_points.txt
|
|
50
53
|
xobjects.egg-info/requires.txt
|
|
51
|
-
xobjects.egg-info/top_level.txt
|
|
54
|
+
xobjects.egg-info/top_level.txt
|
|
55
|
+
xobjects/headers/atomicadd.h
|
|
56
|
+
xobjects/headers/common.h
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.5.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|