与自定义迭代器的性能差异

时间:2016-05-17 17:07:56

标签: c++ performance gcc

我试图在3D图像的像素上实现迭代器。图像存储在一个矢量中,我做索引映射从(x,y,z)到矢量索引。我们的想法是能够在图像上进行基于范围的处理。

当然,使用向量迭代器迭代像素非常快(在我的情况下为0.6秒),但我无法访问索引。

三重for循环(z,y,x在内存顺序中)在VS2015上同样快,但在gcc或clang(gcc 5.2,clang 3.9以及旧版本。)

我编写了一个返回元组(x,y,z,值)的迭代器,它也是2.4s左右,即使使用VS2015也是如此。迭代器包含对图像的引用,以及三个字段x,y和z,并且通过使用与三重循环相同的pixel_at(x, y, z)来获得像素的值。

所以,我有两个问题:

  • 为什么VS2015比GCC和Clang快得多? (在编辑中回答)

  • 如何在仍然可以访问x,y,z时实现更快的时间?

这是x / y / z迭代器的增量:

_x++;
if (_x == _im.width())
{
    _x = 0;
    _y++;
    if (_y == _im.height())
    {
        _y = 0;
        _z++;
    }
}

测量的循环如下:

for - 循环:

for (int k = 0; k < im.depth(); ++k)
    for (int j = 0; j < im.height(); ++j)
        for (int i = 0; i < im.width(); ++i)
            sum += im.pixel_at(i, j, k);

迭代向量:

for (unsigned char c : im.pixels())
    sum += c;

使用自定义迭代器迭代:

for (const auto& c : im.indexes())
    sum += std::get<3>(c);

使用单个索引实现的迭代器为我提供了良好的性能(0.6s),但是我无法访问x,y,z,并且在运行中计算它们太慢(6s)。在迭代器中使用x,y,z和索引也无济于事(2.4s)。

基准是图像上的100次传递,同时对像素的值进行求和,随机初始化。对于我也有索引的情况,我确保读取索引的值(例如,z坐标的总和),以便编译器不优化它们。它不会改变任何东西,除了我在运行中计算索引的情况,其中计算被优化掉了。

整个代码可以在这里找到:https://github.com/nitnelave/3DIterator

谢谢!

编辑:启用链接时优化(-flto)后,三元for循环在g ++上与向量迭代器一样快(0.19s)(0.18s) ),但自定义迭代器仍然慢6倍(1.2秒)。这已经比我在for循环和迭代器中得到的3.2s要好得多,但我们还没到那里。

编辑:我隔离了函数中的循环,我禁止gcc内联,以隔离汇编代码,这里是:

Triple for循环:

push   %r15
push   %r14
push   %r13
push   %r12
push   %rbp
push   %rdi
push   %rsi
push   %rbx
sub    $0x78,%rsp
mov    0x8(%rcx),%eax
pxor   %xmm4,%xmm4
mov    %rcx,%r12
movl   $0x64,0x5c(%rsp)

xor    %r13d,%r13d
xor    %r14d,%r14d
mov    %eax,0x58(%rsp)
mov    0x58(%rsp),%edx
test   %edx,%edx
jle    10040163a <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2fa>
mov    0x4(%r12),%eax
movl   $0x0,0x54(%rsp)

movl   $0x0,0x2c(%rsp)

mov    %eax,0x48(%rsp)
nopl   0x0(%rax,%rax,1)
nopw   %cs:0x0(%rax,%rax,1)

mov    0x48(%rsp),%eax
test   %eax,%eax
jle    10040161f <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2df>
movslq (%r12),%rax
mov    0x54(%rsp),%r10d
mov    0x2c(%rsp),%ebx
mov    %rax,%rcx
mov    %rax,0x40(%rsp)
add    %rax,%rax
mov    %rax,0x38(%rsp)
lea    -0x1(%rcx),%eax
imul   %ecx,%r10d
imul   %eax,%ebx
mov    %eax,0x50(%rsp)
movslq %r10d,%rsi
lea    (%rsi,%rsi,1),%r9
mov    %ebx,0x4c(%rsp)
xor    %ebx,%ebx
xchg   %ax,%ax
nopw   %cs:0x0(%rax,%rax,1)

test   %ecx,%ecx
jle    100401605 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2c5>
mov    0x10(%r12),%rdx
lea    (%rdx,%r9,1),%r8
mov    %r8,%rax
and    $0xf,%eax
shr    %rax
neg    %rax
and    $0x7,%eax
cmp    %ecx,%eax
cmova  %ecx,%eax
cmp    $0xa,%ecx
cmovbe %ecx,%eax
test   %eax,%eax
je     100401690 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x350>
movswl (%r8),%r8d
add    %r8d,%r14d
cmp    $0x1,%eax
je     100401720 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x3e0>
movswl 0x2(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x2,%eax
je     100401710 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x3d0>
movswl 0x4(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x3,%eax
je     100401700 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x3c0>
movswl 0x6(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x4,%eax
je     1004016f0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x3b0>
movswl 0x8(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x5,%eax
je     1004016e0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x3a0>
movswl 0xa(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x6,%eax
je     1004016d0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x390>
movswl 0xc(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x7,%eax
je     1004016c0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x380>
movswl 0xe(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0x8,%eax
je     1004016b0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x370>
movswl 0x10(%rdx,%r9,1),%r8d
add    %r8d,%r14d
cmp    $0xa,%eax
jne    1004016a0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x360>
movswl 0x12(%rdx,%r9,1),%r8d
add    %r8d,%r14d
mov    $0xa,%r8d
cmp    %ecx,%eax
je     1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb>
mov    %eax,%r15d
mov    %ecx,%edi
sub    %eax,%edi
mov    %r15,0x30(%rsp)
mov    0x50(%rsp),%r15d
lea    -0x8(%rdi),%r11d
sub    %eax,%r15d
shr    $0x3,%r11d
add    $0x1,%r11d
cmp    $0x6,%r15d
lea    0x0(,%r11,8),%ebp

jbe    100401573 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x233>
mov    0x30(%rsp),%r15
pxor   %xmm0,%xmm0
xor    %eax,%eax
add    %rsi,%r15
lea    (%rdx,%r15,2),%r15
movdqa (%r15),%xmm1
movdqa %xmm4,%xmm3
add    $0x1,%eax
add    $0x10,%r15
pcmpgtw %xmm1,%xmm3
movdqa %xmm1,%xmm2
cmp    %eax,%r11d
punpcklwd %xmm3,%xmm2
punpckhwd %xmm3,%xmm1
paddd  %xmm2,%xmm0
paddd  %xmm1,%xmm0
ja     10040151a <_Z17bench_triple_loopRK7Image3D.constprop.5+0x1da>
movdqa %xmm0,%xmm1
add    %ebp,%r8d
psrldq $0x8,%xmm1
 paddd  %xmm1,%xmm0
 movdqa %xmm0,%xmm1
 psrldq $0x4,%xmm1
 paddd  %xmm1,%xmm0
 movd   %xmm0,%eax
 add    %eax,%r14d
 cmp    %ebp,%edi
 je     1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 lea    (%r10,%r8,1),%eax
 cltq
 movswl (%rdx,%rax,2),%eax
 add    %eax,%r14d
 lea    0x1(%r8),%eax
 cmp    %eax,%ecx
 jle    1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 add    %r10d,%eax
 cltq
 movswl (%rdx,%rax,2),%eax
 add    %eax,%r14d
 lea    0x2(%r8),%eax
 cmp    %eax,%ecx
 jle    1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 add    %r10d,%eax
 cltq
 movswl (%rdx,%rax,2),%eax
 add    %eax,%r14d
 lea    0x3(%r8),%eax
 cmp    %eax,%ecx
 jle    1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 add    %r10d,%eax
 cltq
 movswl (%rdx,%rax,2),%eax
 add    %eax,%r14d
 lea    0x4(%r8),%eax
 cmp    %eax,%ecx
 jle    1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 add    %r10d,%eax
 cltq
 movswl (%rdx,%rax,2),%eax
 add    %eax,%r14d
 lea    0x5(%r8),%eax
 cmp    %eax,%ecx
 jle    1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 add    %r10d,%eax
 add    $0x6,%r8d
 cltq
 movswl (%rdx,%rax,2),%eax
 add    %eax,%r14d
 cmp    %r8d,%ecx
 jle    1004015fb <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2bb
 add    %r10d,%r8d
 movslq %r8d,%r8
 movswl (%rdx,%r8,2),%eax
 add    %eax,%r14d
 add    0x2c(%rsp),%r13d
 add    0x4c(%rsp),%r13d
 add    $0x1,%ebx
 add    0x38(%rsp),%r9
 add    0x40(%rsp),%rsi
 add    %ecx,%r10d
 cmp    %ebx,0x48(%rsp)
 jne    1004013f0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0xb0>
 addl   $0x1,0x2c(%rsp)
 mov    0x48(%rsp),%ebx
 mov    0x2c(%rsp),%eax
 add    %ebx,0x54(%rsp)
 cmp    0x58(%rsp),%eax
 jne    1004013a0 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x60>
 subl   $0x1,0x5c(%rsp)
 jne    10040136c <_Z17bench_triple_loopRK7Image3D.constprop.5+0x2c>
mov    0x2a64(%rip),%rcx        # 1004040b0 <__fu0__ZSt4cout>
mov    %r13d,%eax
mov    %r14d,%r13d
mov    %eax,%edx
callq  100401828 <_ZNSolsEi>
lea    0x6f(%rsp),%rdx
mov    $0x1,%r8d
mov    %rax,%rcx
movb   $0xa,0x6f(%rsp)
callq  100401800 <_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l>
mov    %r13d,%eax
add    $0x78,%rsp
pop    %rbx
pop    %rsi
pop    %rdi
pop    %rbp
pop    %r12
pop    %r13
pop    %r14
pop    %r15
retq
nop
nopw   %cs:0x0(%rax,%rax,1)

xor    %r8d,%r8d
jmpq   1004014da <_Z17bench_triple_loopRK7Image3D.constprop.5+0x19a>
nopl   0x0(%rax,%rax,1)

mov    $0x9,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x8,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x7,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x6,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x5,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x4,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x3,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x2,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)
mov    $0x1,%r8d
jmpq   1004014d2 <_Z17bench_triple_loopRK7Image3D.constprop.5+0x192>
nopl   0x0(%rax,%rax,1)

迭代向量:

push   %r14
push   %r13
push   %r12
push   %rbp
push   %rdi
push   %rsi
push   %rbx
mov    0x10(%rcx),%r8
mov    0x18(%rcx),%r10
mov    $0x64,%ebx
pxor   %xmm4,%xmm4
lea    0x2(%r8),%r12
mov    %r10,%rdi
mov    %r8,%rbp
and    $0xf,%ebp
sub    %r12,%rdi
shr    %rbp
shr    %rdi
neg    %rbp
lea    0x1(%rdi),%r11
and    $0x7,%ebp
cmp    %r11,%rbp
cmova  %r11,%rbp
xor    %eax,%eax
xchg   %ax,%ax
nopw   %cs:0x0(%rax,%rax,1)

cmp    %r8,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
cmp    $0xa,%r11
mov    %r11,%rcx
ja     1004012f0 <_Z10bench_iterRK7Image3D.constprop.4+0x210>
movswl (%r8),%edx
add    %edx,%eax
cmp    $0x1,%rcx
je     100401300 <_Z10bench_iterRK7Image3D.constprop.4+0x220>
movswl 0x2(%r8),%edx
add    %edx,%eax
cmp    $0x2,%rcx
lea    0x4(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0x4(%r8),%edx
add    %edx,%eax
cmp    $0x3,%rcx
lea    0x6(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0x6(%r8),%edx
add    %edx,%eax
cmp    $0x4,%rcx
lea    0x8(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0x8(%r8),%edx
add    %edx,%eax
cmp    $0x5,%rcx
lea    0xa(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0xa(%r8),%edx
add    %edx,%eax
cmp    $0x6,%rcx
lea    0xc(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0xc(%r8),%edx
add    %edx,%eax
cmp    $0x7,%rcx
lea    0xe(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0xe(%r8),%edx
add    %edx,%eax
cmp    $0x8,%rcx
lea    0x10(%r8),%rdx
je     1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0x10(%r8),%edx
add    %edx,%eax
cmp    $0xa,%rcx
lea    0x12(%r8),%rdx
jne    1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
movswl 0x12(%r8),%edx
add    %edx,%eax
lea    0x14(%r8),%rdx
cmp    %rcx,%r11
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
mov    %r11,%rsi
mov    %rdi,%r14
sub    %rcx,%rsi
sub    %rcx,%r14
lea    -0x8(%rsi),%r9
shr    $0x3,%r9
add    $0x1,%r9
cmp    $0x6,%r14
lea    0x0(,%r9,8),%r13

jbe    10040127d <_Z10bench_iterRK7Image3D.constprop.4+0x19d>
pxor   %xmm0,%xmm0
lea    (%r8,%rcx,2),%r14
xor    %ecx,%ecx
movdqa (%r14),%xmm1
add    $0x1,%rcx
movdqa %xmm4,%xmm2
add    $0x10,%r14
movdqa %xmm1,%xmm3
cmp    %rcx,%r9
pcmpgtw %xmm1,%xmm2
punpcklwd %xmm2,%xmm3
punpckhwd %xmm2,%xmm1
paddd  %xmm3,%xmm0
paddd  %xmm1,%xmm0
ja     100401226 <_Z10bench_iterRK7Image3D.constprop.4+0x146>
movdqa %xmm0,%xmm1
lea    (%rdx,%r13,2),%rdx
psrldq $0x8,%xmm1
paddd  %xmm1,%xmm0
movdqa %xmm0,%xmm1
psrldq $0x4,%xmm1
paddd  %xmm1,%xmm0
movd   %xmm0,%ecx
add    %ecx,%eax
cmp    %r13,%rsi
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl (%rdx),%ecx
add    %ecx,%eax
lea    0x2(%rdx),%rcx
cmp    %rcx,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl 0x2(%rdx),%ecx
add    %ecx,%eax
lea    0x4(%rdx),%rcx
cmp    %rcx,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl 0x4(%rdx),%ecx
add    %ecx,%eax
lea    0x6(%rdx),%rcx
cmp    %rcx,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl 0x6(%rdx),%ecx
add    %ecx,%eax
lea    0x8(%rdx),%rcx
cmp    %rcx,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl 0x8(%rdx),%ecx
add    %ecx,%eax
lea    0xa(%rdx),%rcx
cmp    %rcx,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl 0xa(%rdx),%ecx
add    %ecx,%eax
lea    0xc(%rdx),%rcx
cmp    %rcx,%r10
je     1004012dc <_Z10bench_iterRK7Image3D.constprop.4+0x1fc>
movswl 0xc(%rdx),%edx
add    %edx,%eax
sub    $0x1,%ebx
jne    100401130 <_Z10bench_iterRK7Image3D.constprop.4+0x50>
pop    %rbx
pop    %rsi
pop    %rdi
pop    %rbp
pop    %r12
pop    %r13
pop    %r14
retq
test   %rbp,%rbp
jne    100401308 <_Z10bench_iterRK7Image3D.constprop.4+0x228>
xor    %ecx,%ecx
mov    %r8,%rdx
jmpq   1004011f6 <_Z10bench_iterRK7Image3D.constprop.4+0x116>
nop
mov    %r12,%rdx
jmpq   1004011ed <_Z10bench_iterRK7Image3D.constprop.4+0x10d>
mov    %rbp,%rcx
jmpq   100401146 <_Z10bench_iterRK7Image3D.constprop.4+0x66>

自定义迭代器:

push   %r14
push   %rbp
push   %rdi
push   %rsi
push   %rbx
sub    $0x30,%rsp
mov    (%rcx),%r10d
mov    $0x64,%ebp
xor    %ebx,%ebx
mov    %rcx,%rdi
mov    0x4(%rcx),%ecx
xor    %edx,%edx
mov    0x8(%rdi),%esi
nop
xor    %r9d,%r9d
xor    %r11d,%r11d
xor    %r8d,%r8d
nopl   0x0(%rax)
cmp    %r9d,%esi
je     1004017b0 <_Z16bench_index_iterRK7Image3D.constprop.0+0x80>
mov    %ecx,%eax
mov    0x10(%rdi),%r14
add    %r9d,%edx
imul   %r9d,%eax
add    %r11d,%eax
imul   %r10d,%eax
add    %r8d,%eax
add    $0x1,%r8d
cltq
movswl (%r14,%rax,2),%eax
add    %eax,%ebx
cmp    %r10d,%r8d
jne    100401760 <_Z16bench_index_iterRK7Image3D.constprop.0+0x30>
add    $0x1,%r11d
xor    %r8d,%r8d
cmp    %r11d,%ecx
jne    100401760 <_Z16bench_index_iterRK7Image3D.constprop.0+0x30>
add    $0x1,%r9d
xor    %r11d,%r11d
cmp    %r9d,%esi
jne    100401765 <_Z16bench_index_iterRK7Image3D.constprop.0+0x35>
nopw   %cs:0x0(%rax,%rax,1)

test   %r11d,%r11d
jne    100401765 <_Z16bench_index_iterRK7Image3D.constprop.0+0x35>
test   %r8d,%r8d
jne    100401765 <_Z16bench_index_iterRK7Image3D.constprop.0+0x35>
sub    $0x1,%ebp
jne    100401750 <_Z16bench_index_iterRK7Image3D.constprop.0+0x20>
mov    0x28ea(%rip),%rcx        # 1004040b0 <__fu0__ZSt4cout>
callq  100401828 <_ZNSolsEi>
lea    0x2f(%rsp),%rdx
mov    $0x1,%r8d
mov    %rax,%rcx
movb   $0xa,0x2f(%rsp)
callq  100401800 <_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l>
mov    %ebx,%eax
add    $0x30,%rsp
pop    %rbx
pop    %rsi
pop    %rdi
pop    %rbp
pop    %r14
retq

所以看起来前两个工作台有循环展开,但不适用于自定义迭代器。

编辑:我也尝试使用boost::asio::coroutine,但速度比我的解决方案慢。

1 个答案:

答案 0 :(得分:1)

为什么慢一点

正如您在反汇编中很好地展示的那样,编译器为不同的循环生成了截然不同的代码。自定义迭代器生成的机器代码的主要缺点是,它不使用SIMD指令。

编译器优化非常复杂。您可以花几天时间查看intermediate optimization steps。此外,不同编译器(和编译器版本)之间的性能也各不相同。在我的测试中,clang是唯一为迭代器生成快速SIMD代码的编译器。

你已经完全正确地做了一件事:测量。如果该部分的表现对您很重要,您必须对其进行衡量。

如何加快速度

基本思想是通过循环帮助编译器。例如结束条件。编译器需要能够证明它没有改变。您可以尝试在本地保留const副本图像的宽度/高度。不幸的是,我无法在您的示例代码上实现显着的加速。

另一个问题是结束迭代器。您需要在每次迭代中检查三个条件,即使只有一个(z)很重要。这使我们得到了替代的循环结束条件,例如哨兵。

范围-V3

使用range-v3可以实现更高效和优雅的循环。我快速尝试了你的代码:

#pragma once

#include "Image3D.h"

#include <range/v3/all.hpp>

template <typename Image>
class range_3d : public ranges::view_facade<range_3d<Image>>
{
    using data_t = typename Image::data_t;
    friend ranges::range_access;

    int x_ = 0;
    int y_ = 0;
    int z_ = 0;
    // Note: This cannot be a reference, because ranges needs a default constructor
    // If it doesn't get one, the compiler errors will haunt you in your dreams.
    Image* im_;

    data_t const& get() const
    {
        return im_->pixel_at(x_, y_, z_);
    }

    bool done() const
    {
        return z_ == im_->depth();
    }

    void next()
    {
        x_++;
        if (x_ == im_->width())
        {
            x_ = 0;
            y_++;
            if (y_ == im_->height())
            {
                y_ = 0;
                z_++;
            }
        }
    }

public:
    range_3d() = default;
    explicit range_3d(Image& im)
    : im_(&im)
    {
    }
};

// use like:
range_3d<Image3D> r(im);
ranges::for_each(r, [&sum](unsigned char c) {
    sum += c;
});

不幸的是它仍然没有提供完美的性能,但至少代码更好(只要你没有得到编译器错误)。

上下文很重要

您必须记住,当您实际使用x/y/z时,迭代器的性能可能会有很大差异。如果您的编译器无法执行总结向量优化,则迭代器可能会显示更多类似的性能。

按需x / y / z计算

我仍然认为你可能想要考虑不保留单独的循环变量。也许您甚至可以优化具有两种幂的图像,或者以某种方式阻止循环,这样您就可以使用位操作从索引中非常有效地计算x / y / z。

零开销抽象

我最喜欢C ++的一个属性是它允许你创建很好的抽象,绝对没有运行时开销。在这种情况下,我/编译器失败让我很痛苦。我觉得这个答案缺少 happy-end ,所以我也鼓励大家尝试这个。