- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
是否可以在 C 或 Cython 中使用自定义大小数据类型(3/5/6/7 字节)的数组?
我在尝试编写复杂算法时遇到了内存效率低下的问题。该算法需要存储数量惊人的数据。所有数据都排列在一个连续的内存块中(如数组)。数据只是一长串 [通常] 非常大的数字。给定一组特定的数字,此列表/数组中的数字类型是常量(它们几乎作为常规 C 数组运行,其中所有数字在数组中的类型相同)
有时以标准数据大小存储每个数字效率不高。通常正常的数据类型是 char、short、int、long 等...但是,如果我使用 int 数组来存储仅在可以存储在 3 个字节的范围内的数据类型,则在每个数字上我丢失 1 个字节的空间。这会导致极低的效率,当您存储数百万个数字时,会破坏内存。不幸的是,没有其他方法可以实现算法的解决方案,我相信自定义数据大小的粗略实现是唯一的方法。
我曾尝试使用 char 数组来完成此任务,但在大多数情况下,在不同的 0 - 255 值位之间转换以形成更大的数据类型效率很低。通常,有一种数学方法可以获取字符并将它们打包成一个更大的数字,或者获取那个更大的数字,然后划分它的各个字符。这是我尝试使用 Cython 编写的一种极其低效的算法:
def to_bytes(long long number, int length):
cdef:
list chars = []
long long m
long long d
for _ in range(length):
m = number % 256
d = number // 256
chars.append(m)
number = d
cdef bytearray binary = bytearray(chars)
binary = binary[::-1]
return binary
def from_bytes(string):
cdef long long d = int(str(string).encode('hex'), 16)
return d
请记住,我并不完全想要改进此算法,而是一种声明特定数据类型数组的基本方法,因此我不必进行此转换。
最佳答案
我认为重要的问题是您是否需要同时访问所有数据。
如果您只需要同时访问一个数据 block
如果您一次只需要访问一个数组,那么 Pythonic 的一种可能性是使用数据类型为 uint8
并根据需要使用宽度的 NumPy 数组。当您需要对数据进行操作时,将压缩数据扩展(这里是 3 个八位字节数字到 uint32
):
import numpy as np
# in this example `compressed` is a Nx3 array of octets (`uint8`)
expanded = np.empty((compressed.shape[0], 4))
expanded[:,:3] = compressed
expanded[:, 3] = 0
expanded = expanded.view('uint32').reshape(-1)
然后对 expanded
执行操作,它是 N 个 uint32
值的一维 vector 。
完成后,数据可以保存回来:
# recompress
compressed[:] = expanded.view('uint8').reshape(-1,4)[:,:3]
在上面的示例中,每个方向所花费的时间(在我使用 Python 的机器中)大约为每个元素 8 纳秒。在这里使用 Cython 可能不会带来太大的性能优势,因为几乎所有时间都花在了在 NumPy 的黑暗深处某处的缓冲区之间复制数据。
这是一个很高的一次性成本,但如果您计划至少访问每个元素一次,那么支付一次性成本可能比为每个操作支付类似成本要便宜。
当然在C中也可以采用同样的做法:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/resource.h>
#define NUMITEMS 10000000
int main(void)
{
uint32_t *expanded;
uint8_t * cmpressed, *exp_as_octets;
struct rusage ru0, ru1;
uint8_t *ep, *cp, *end;
double time_delta;
// create some compressed data
cmpressed = (uint8_t *)malloc(NUMITEMS * 3);
getrusage(RUSAGE_SELF, &ru0);
// allocate the buffer and copy the data
exp_as_octets = (uint8_t *)malloc(NUMITEMS * 4);
end = exp_as_octets + NUMITEMS * 4;
ep = exp_as_octets;
cp = cmpressed;
while (ep < end)
{
// copy three octets out of four
*ep++ = *cp++;
*ep++ = *cp++;
*ep++ = *cp++;
*ep++ = 0;
}
expanded = (uint32_t *)exp_as_octets;
getrusage(RUSAGE_SELF, &ru1);
printf("Uncompress\n");
time_delta = ru1.ru_utime.tv_sec + ru1.ru_utime.tv_usec * 1e-6
- ru0.ru_utime.tv_sec - ru0.ru_utime.tv_usec * 1e-6;
printf("User: %.6lf seconds, %.2lf nanoseconds per element", time_delta, 1e9 * time_delta / NUMITEMS);
time_delta = ru1.ru_stime.tv_sec + ru1.ru_stime.tv_usec * 1e-6
- ru0.ru_stime.tv_sec - ru0.ru_stime.tv_usec * 1e-6;
printf("System: %.6lf seconds, %.2lf nanoseconds per element", time_delta, 1e9 * time_delta / NUMITEMS);
getrusage(RUSAGE_SELF, &ru0);
// compress back
ep = exp_as_octets;
cp = cmpressed;
while (ep < end)
{
*cp++ = *ep++;
*cp++ = *ep++;
*cp++ = *ep++;
ep++;
}
getrusage(RUSAGE_SELF, &ru1);
printf("Compress\n");
time_delta = ru1.ru_utime.tv_sec + ru1.ru_utime.tv_usec * 1e-6
- ru0.ru_utime.tv_sec - ru0.ru_utime.tv_usec * 1e-6;
printf("User: %.6lf seconds, %.2lf nanoseconds per element", time_delta, 1e9 * time_delta / NUMITEMS);
time_delta = ru1.ru_stime.tv_sec + ru1.ru_stime.tv_usec * 1e-6
- ru0.ru_stime.tv_sec - ru0.ru_stime.tv_usec * 1e-6;
printf("System: %.6lf seconds, %.2lf nanoseconds per element", time_delta, 1e9 * time_delta / NUMITEMS);
}
此报告:
Uncompress
User: 0.022650 seconds, 2.27 nanoseconds per element
System: 0.016171 seconds, 1.62 nanoseconds per element
Compress
User: 0.011698 seconds, 1.17 nanoseconds per element
System: 0.000018 seconds, 0.00 nanoseconds per element
代码是用 gcc -Ofast
编译的,可能相对接近最佳速度。系统时间花费在 malloc
上。在我看来,这看起来相当快,因为我们正在以 2-3 GB/s 的速度进行内存读取。 (这也意味着虽然使代码成为多线程很容易,但可能不会带来太多速度优势。)
如果您想获得最佳性能,您需要为每个数据宽度分别编写压缩/解压缩例程。 (我不保证上面的 C 代码在任何机器上绝对是最快的,我没有看机器代码。)
如果您需要随机访问单独的值
相反,如果您只需要在这里访问一个值,在那里访问另一个值,Python 将不会提供任何合理快速的方法,因为数组查找开销很大。
在这种情况下,我建议您创建 C 例程来获取和放回数据。请参阅 technosaurus
的回答。技巧有很多,但对齐问题还是无法避免。
读取奇数数组时的一个有用技巧可能是(这里从一个八位组数组压缩
中读取 3 个八位组到 uint32_t
value
):
value = (uint32_t *)&compressed[3 * n] & 0x00ffffff;
然后其他人会处理可能的错位,最后会有一个八位字节的垃圾。不幸的是,这不能在写入值时使用。并且 - 再一次 - 这可能会或可能不会比任何其他替代方案更快或更慢。
关于python - 自定义大小数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24592077/
我正在尝试创建一个包含 int[][] 项的数组 即 int version0Indexes[][4] = { {1,2,3,4}, {5,6,7,8} }; int version1Indexes[
我有一个整数数组: private int array[]; 如果我还有一个名为 add 的方法,那么以下有什么区别: public void add(int value) { array[va
当您尝试在 JavaScript 中将一个数组添加到另一个数组时,它会将其转换为一个字符串。通常,当以另一种语言执行此操作时,列表会合并。 JavaScript [1, 2] + [3, 4] = "
根据我正在阅读的教程,如果您想创建一个包含 5 列和 3 行的表格来表示这样的数据... 45 4 34 99 56 3 23 99 43 2 1 1 0 43 67 ...它说你可以使用下
我通常使用 python 编写脚本/程序,但最近开始使用 JavaScript 进行编程,并且在使用数组时遇到了一些问题。 在 python 中,当我创建一个数组并使用 for x in y 时,我得
我有一个这样的数组: temp = [ 'data1', ['data1_a','data1_b'], ['data2_a','data2_b','data2_c'] ]; // 我想使用 toStr
rent_property (table name) id fullName propertyName 1 A House Name1 2 B
这个问题在这里已经有了答案: 关闭13年前。 Possible Duplicate: In C arrays why is this true? a[5] == 5[a] array[index] 和
使用 Excel 2013。经过多年的寻找和适应,我的第一篇文章。 我正在尝试将当前 App 用户(即“John Smith”)与他的电子邮件地址“jsmith@work.com”进行匹配。 使用两个
当仅在一个边距上操作时,apply 似乎不会重新组装 3D 数组。考虑: arr 1),但对我来说仍然很奇怪,如果一个函数返回一个具有尺寸的对象,那么它们基本上会被忽略。 最佳答案 这是一个不太理
我有一个包含 GPS 坐标的 MySQL 数据库。这是我检索坐标的部分 PHP 代码; $sql = "SELECT lat, lon FROM gps_data"; $stmt=$db->query
我需要找到一种方法来执行这个操作,我有一个形状数组 [批量大小, 150, 1] 代表 batch_size 整数序列,每个序列有 150 个元素长,但在每个序列中都有很多添加的零,以使所有序列具有相
我必须通过 url 中的 json 获取文本。 层次结构如下: 对象>数组>对象>数组>对象。 我想用这段代码获取文本。但是我收到错误 :org.json.JSONException: No valu
enter code here- (void)viewDidLoad { NSMutableArray *imageViewArray= [[NSMutableArray alloc] init];
知道如何对二维字符串数组执行修剪操作,例如使用 Java 流 API 进行 3x3 并将其收集回相同维度的 3x3 数组? 重点是避免使用显式的 for 循环。 当前的解决方案只是简单地执行一个 fo
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
我有来自 ASP.NET Web 服务的以下 XML 输出: 1710 1711 1712 1713
如果我有一个对象todo作为您状态的一部分,并且该对象包含数组列表,则列表内部有对象,在这些对象内部还有另一个数组listItems。如何更新数组 listItems 中 id 为“poi098”的对
我想将最大长度为 8 的 bool 数组打包成一个字节,通过网络发送它,然后将其解压回 bool 数组。已经在这里尝试了一些解决方案,但没有用。我正在使用单声道。 我制作了 BitArray,然后尝试
我们的数据库中有这个字段指示一周中的每一天的真/假标志,如下所示:'1111110' 我需要将此值转换为 boolean 数组。 为此,我编写了以下代码: char[] freqs = weekday
我是一名优秀的程序员,十分优秀!