aligned_memory_mt.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #![allow(dead_code)]
  2. /* This file is almost identical to the aligned_memory.rs file in the
  3. spiral-rs crate. The name is modified from AlignedMemory to
  4. AlignedMemoryMT, and there is one (unsafe!) change to the API:
  5. pub unsafe fn as_mut_ptr(&mut self) -> *mut u64
  6. has changed to:
  7. pub unsafe fn as_mut_ptr(&self) -> *mut u64
  8. The reason for this change is explicitly to allow multiple threads to
  9. *write* into the memory pool concurrently, with the caveat that the
  10. threads *must not* try to write into the same memory location. In
  11. Spiral, each polynomial created from the database ends up scattered
  12. into noncontiguous words of memory, but any one word still only comes
  13. from one polynomial. So with this change, different threads can read
  14. different parts of the database to produce different polynomials, and
  15. write those polynomials into the same memory pool (but *not* the same
  16. memory locations) at the same time.
  17. */
  18. use std::{
  19. alloc::{alloc_zeroed, dealloc, Layout},
  20. mem::size_of,
  21. ops::{Index, IndexMut},
  22. slice::{from_raw_parts, from_raw_parts_mut},
  23. };
  24. const ALIGN_SIMD: usize = 64; // enough to support AVX-512
  25. pub type AlignedMemoryMT64 = AlignedMemoryMT<ALIGN_SIMD>;
  26. pub struct AlignedMemoryMT<const ALIGN: usize> {
  27. p: *mut u64,
  28. sz_u64: usize,
  29. layout: Layout,
  30. }
  31. impl<const ALIGN: usize> AlignedMemoryMT<{ ALIGN }> {
  32. pub fn new(sz_u64: usize) -> Self {
  33. let sz_bytes = sz_u64 * size_of::<u64>();
  34. let layout = Layout::from_size_align(sz_bytes, ALIGN).unwrap();
  35. let ptr;
  36. unsafe {
  37. ptr = alloc_zeroed(layout);
  38. }
  39. Self {
  40. p: ptr as *mut u64,
  41. sz_u64,
  42. layout,
  43. }
  44. }
  45. // pub fn from(data: &[u8]) -> Self {
  46. // let sz_u64 = (data.len() + size_of::<u64>() - 1) / size_of::<u64>();
  47. // let mut out = Self::new(sz_u64);
  48. // let out_slice = out.as_mut_slice();
  49. // let mut i = 0;
  50. // for chunk in data.chunks(size_of::<u64>()) {
  51. // out_slice[i] = u64::from_ne_bytes(chunk);
  52. // i += 1;
  53. // }
  54. // out
  55. // }
  56. pub fn as_slice(&self) -> &[u64] {
  57. unsafe { from_raw_parts(self.p, self.sz_u64) }
  58. }
  59. pub fn as_mut_slice(&mut self) -> &mut [u64] {
  60. unsafe { from_raw_parts_mut(self.p, self.sz_u64) }
  61. }
  62. pub unsafe fn as_ptr(&self) -> *const u64 {
  63. self.p
  64. }
  65. pub unsafe fn as_mut_ptr(&self) -> *mut u64 {
  66. self.p
  67. }
  68. pub fn len(&self) -> usize {
  69. self.sz_u64
  70. }
  71. }
  72. unsafe impl<const ALIGN: usize> Send for AlignedMemoryMT<{ ALIGN }> {}
  73. unsafe impl<const ALIGN: usize> Sync for AlignedMemoryMT<{ ALIGN }> {}
  74. impl<const ALIGN: usize> Drop for AlignedMemoryMT<{ ALIGN }> {
  75. fn drop(&mut self) {
  76. unsafe {
  77. dealloc(self.p as *mut u8, self.layout);
  78. }
  79. }
  80. }
  81. impl<const ALIGN: usize> Index<usize> for AlignedMemoryMT<{ ALIGN }> {
  82. type Output = u64;
  83. fn index(&self, index: usize) -> &Self::Output {
  84. &self.as_slice()[index]
  85. }
  86. }
  87. impl<const ALIGN: usize> IndexMut<usize> for AlignedMemoryMT<{ ALIGN }> {
  88. fn index_mut(&mut self, index: usize) -> &mut Self::Output {
  89. &mut self.as_mut_slice()[index]
  90. }
  91. }
  92. impl<const ALIGN: usize> Clone for AlignedMemoryMT<{ ALIGN }> {
  93. fn clone(&self) -> Self {
  94. let mut out = Self::new(self.sz_u64);
  95. out.as_mut_slice().copy_from_slice(self.as_slice());
  96. out
  97. }
  98. }