- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
假设我们有一个数据数组和另一个带索引的数组。
data = [1, 2, 3, 4, 5, 7]
index = [5, 1, 4, 0, 2, 3]
我们想从 index
的 data
元素创建一个新数组。结果应该是
[4, 2, 5, 7, 3, 1]
朴素算法适用于 O(N),但它执行随机内存访问。
你能推荐具有相同复杂度的 CPU 缓存友好算法吗?
附言在我的特定情况下,数据数组中的所有元素都是整数。
公务员事务局数组可能包含数百万个元素。
PPPS 我接受 SSE/AVX 或任何其他 x64 特定优化
最佳答案
将索引和数据合并到一个数组中。然后使用一些缓存友好的排序算法对这些对进行排序(按索引)。然后摆脱索引。 (您可以将合并/删除索引与排序算法的第一遍/最后一遍结合起来对此进行一点优化)。
对于缓存友好的 O(N) 排序,使用具有足够小 radix
的基数排序(最多 CPU 缓存中缓存行数的一半)。
这是类基数排序算法的 C 实现:
void reorder2(const unsigned size)
{
const unsigned min_bucket = size / kRadix;
const unsigned large_buckets = size % kRadix;
g_counters[0] = 0;
for (unsigned i = 1; i <= large_buckets; ++i)
g_counters[i] = g_counters[i - 1] + min_bucket + 1;
for (unsigned i = large_buckets + 1; i < kRadix; ++i)
g_counters[i] = g_counters[i - 1] + min_bucket;
for (unsigned i = 0; i < size; ++i)
{
const unsigned dst = g_counters[g_index[i] % kRadix]++;
g_sort[dst].index = g_index[i] / kRadix;
g_sort[dst].value = g_input[i];
__builtin_prefetch(&g_sort[dst + 1].value, 1);
}
g_counters[0] = 0;
for (unsigned i = 1; i < (size + kRadix - 1) / kRadix; ++i)
g_counters[i] = g_counters[i - 1] + kRadix;
for (unsigned i = 0; i < size; ++i)
{
const unsigned dst = g_counters[g_sort[i].index]++;
g_output[dst] = g_sort[i].value;
__builtin_prefetch(&g_output[dst + 1], 1);
}
}
它在两个方面不同于基数排序:(1) 它不进行计数遍历,因为所有计数器都是预先知道的; (2) 避免使用基数的2次方值。
This C++ code was used for benchmarking (如果你想在 32 位系统上运行它,稍微减小 kMaxSize
常量)。
以下是基准测试结果(在具有 6Mb 缓存的 Haswell CPU 上):
很容易看出小型数组(少于 200 万个元素)即使对于朴素算法也是缓存友好的。此外,您可能会注意到排序方法在图表的最后一点开始对缓存不友好(size/radix
在 L3 缓存中接近 0.75 缓存行)。在这些限制之间,排序方法比朴素算法更有效。
理论上(如果我们仅将这些算法所需的内存带宽与 64 字节缓存行和 4 字节值进行比较)排序算法应该快 3 倍。实际上,我们的差异要小得多,大约 20%。如果我们为 data
数组使用更小的 16 位值(在这种情况下,排序算法大约快 1.5 倍),这可能会有所改善。
排序方法的另一个问题是当 size/radix
接近某个 2 的幂时它的最坏情况行为。这可能会被忽略(因为没有那么多“坏”尺寸)或通过使该算法稍微复杂一些来解决。
如果我们将 channel 数增加到 3,则所有 3 个 channel 主要使用 L1 缓存,但内存带宽增加了 60%。我用这段代码得到了实验结果:TL; DR .在(通过实验)确定最佳基数值后,对于大于 4 000 000 的大小(其中 2-pass 算法使用 L3 缓存一次)我得到了更好的结果,但对于较小的数组(其中 2-pass 算法使用 L2缓存两次)。正如预期的那样,16 位数据的性能更好。
结论:性能差异远小于算法复杂度差异,因此朴素方法几乎总是更好;如果性能非常重要并且只使用 2 或 4 字节值,则排序方法更可取。
关于通过已知索引、收集、分散重新调整的数组缓存友好复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34693853/
将此矩阵视为一个内存。 p00 p01 p02 p03 p04 p05 p06.... p0n
一般,您如何确定如何根据已知模式迭代数组? 具体,这是我想要迭代的模式,返回一个数组数组:(每个数字都是原始数组的索引值:[0, 1, 2, 3] ) 0 0,1 0,1,2 0,1,2,3 1 1,
问题: 我正在编写一个 C++ 程序,我想在其中从 TCP/IP 套接字读取数据流。数据由几个不同长度和数据类型的数据包组成,但是,它们都是以十六进制格式接收的。在此图中可以看到数据包的长度及其数据类
使用 VC12(在 Visual Studio 2013 RTM 中)[1] 编译此程序会导致崩溃(在所有构建配置中),而实际上它不应该: #include void foo(std::string
我有一个 Snakemake 规则,适用于数据存档并本质上解压其中的数据。文件包含我在规则开始之前知道的不同数量的文件,因此我想利用它并执行类似的操作 rule unpack: input:
有这样的 list 吗? 我对 iOS 开发比较陌生,我认为研究最知名的编译器错误或陷阱列表会很棒。 编辑: 今天我花了太多时间来理解这样的代码发生了什么: 在 *.h @interface I :
如何选择已知 div 中的最后一个子元素,其中该子元素是未知元素。即:元素可以是段落或无序列表。 大多数情况下结构是: Text 但在其他情况下,结构将是: Text More Text
我想绘制以下内容: x = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] y = [10, 20, 1, 8, 7, 2, 3, 7, 11] 作为条形图
关于未知列表的排序问题,人们已经知道很多了。但是,在堆栈机器中找到已知列表的最佳排序问题呢?也就是说,假设您有以下堆栈计算机: [4,1,3,2] [] [] 也就是说,有3个堆栈的空间,其中1个堆栈
正如主题中所写,我必须使用 mechanize 更改某些输入字段的值,但我没有它的名称只有 id:/让我们坚持这一点。 表单是这样的:
我只是回想起我的大学类(class),想知道这里是否有人在专业环境中使用过“Z 符号”。老实说,这是我一生中参加过的最无聊的类(class)。也许是因为老师,但当时我们真的都认为这是浪费时间。我可能错
我正在尝试编写一个函数来获取 Windows 等效的 HOME。我的 C 技能生疏了,所以请不要介意我的示例代码无法编译。我试图在 Windows Vista 和更新版本上使用 SHGetKnownF
我想找到一个正整数矩阵B,使得AB = BC,其中A和C是具有共同特征值的正整数矩阵。对于这种情况,存在解,但不唯一;我只需要一种解决方案。 有人知道 python 或 matlab 中可以执行此操作
如果您有两个二进制 blob,x 和 y。然后将它们散列在一起,假设使用 SHA-512。入侵者知道 y,这会使反转哈希变得容易多少? 是否有关于 y 有多大并且可以与 x 比较才成为问题的指南?这有
我正在使用Angular-Stripe-Checkout library创建像这样的 stripeToken example 。一些亮点如下所示。 与许多 Angular-stripe 库和示例一样,
我有一个带有 (e,n) 加密数据的公钥,必须通过 RSA 获取纯文本,并且所有这些都在 C 中! 首先我想知道如何找出我的 p 和 q 是什么?我知道它们必须是质数和 p<>q! 最佳答案 首先,因
表1(客户表) Id, CustomerId, IsKnownCustomer,phonemacaddress 1, 空 0 00:9a:34:cf:a4 2, 004024 1 00:6f:64:c
问题是找到第 n-th Catalan 数 mod m,其中 m 是 NOT prime , m = (10^14 + 7)。以下是我尝试过的方法列表:(max N = 10,000) 查表的动态编程
每当我打开我的应用程序时,我都想将我的应用程序连接到一个已知的 wifi 网络/ssid。即使手机当前通过 3G 或任何其他 wifi 网络连接。 仅使用 phonegap/html5 是否可行? 最
我正在做一个项目,我想为特定的用户组(具有管理员角色)实现实时通知,经过一些研究,我明白我需要 session 才能知道哪些用户已登录(默认情况下他们是匿名的)。 另外,我只需要向特定用户发送通知。
我是一名优秀的程序员,十分优秀!