Thread overview
Performance of map!()
Apr 24, 2015
Chris
Apr 24, 2015
John Colvin
Apr 24, 2015
Chris
Apr 24, 2015
Casper Færgemand
Apr 24, 2015
Chris
Apr 24, 2015
Casper Færgemand
Apr 24, 2015
John Colvin
Apr 24, 2015
Chris
Apr 24, 2015
Marc Schütz
April 24, 2015
I replaced a range that was similar to map with map and the performance dropped by ~0.5 msec.

The range I used previously is based on Adam's D Cookbook. It is consistently faster than map.

private struct Transformer(alias agent, R) if (isInputRange!R) {

  private R r;
  this (R r) {
    this.r = r;
  }

  static if (isInfinite!R) {
    enum bool empty = false;
  }
  else {
    @property bool empty() {
      return r.empty;
    }
  }

  void popFront() {
    r.popFront();

  }
  @property ref auto front() {
    return agent(r.front);
  }

  static if (isForwardRange!R) {
    @property auto save() {
      return Transformer!(agent, R)(r.save);
    }
  }

  static if (isBidirectionalRange!R) {
    @property auto back() {
      return agent(r.back);
    }
    void popBack() {
      r.popBack();
    }
  }

  static if (isRandomAccessRange!R) {
    auto opIndex(size_t i) {
      return agent(r[i]);
    }
  }

  static if (hasLength!R) {
    @property auto length() {
      return r.length;
    }
  }
}

April 24, 2015
On Friday, 24 April 2015 at 10:22:05 UTC, Chris wrote:
> I replaced a range that was similar to map with map and the performance dropped by ~0.5 msec.
>
> The range I used previously is based on Adam's D Cookbook. It is consistently faster than map.
>
> private struct Transformer(alias agent, R) if (isInputRange!R) {
>
>   private R r;
>   this (R r) {
>     this.r = r;
>   }
>
>   static if (isInfinite!R) {
>     enum bool empty = false;
>   }
>   else {
>     @property bool empty() {
>       return r.empty;
>     }
>   }
>
>   void popFront() {
>     r.popFront();
>
>   }
>   @property ref auto front() {
>     return agent(r.front);
>   }
>
>   static if (isForwardRange!R) {
>     @property auto save() {
>       return Transformer!(agent, R)(r.save);
>     }
>   }
>
>   static if (isBidirectionalRange!R) {
>     @property auto back() {
>       return agent(r.back);
>     }
>     void popBack() {
>       r.popBack();
>     }
>   }
>
>   static if (isRandomAccessRange!R) {
>     auto opIndex(size_t i) {
>       return agent(r[i]);
>     }
>   }
>
>   static if (hasLength!R) {
>     @property auto length() {
>       return r.length;
>     }
>   }
> }

Compiler? Compiler flags?
April 24, 2015
On Friday, 24 April 2015 at 10:53:04 UTC, John Colvin wrote:
> On Friday, 24 April 2015 at 10:22:05 UTC, Chris wrote:
>> I replaced a range that was similar to map with map and the performance dropped by ~0.5 msec.
>>
>> The range I used previously is based on Adam's D Cookbook. It is consistently faster than map.
>>
>> private struct Transformer(alias agent, R) if (isInputRange!R) {
>>
>>  private R r;
>>  this (R r) {
>>    this.r = r;
>>  }
>>
>>  static if (isInfinite!R) {
>>    enum bool empty = false;
>>  }
>>  else {
>>    @property bool empty() {
>>      return r.empty;
>>    }
>>  }
>>
>>  void popFront() {
>>    r.popFront();
>>
>>  }
>>  @property ref auto front() {
>>    return agent(r.front);
>>  }
>>
>>  static if (isForwardRange!R) {
>>    @property auto save() {
>>      return Transformer!(agent, R)(r.save);
>>    }
>>  }
>>
>>  static if (isBidirectionalRange!R) {
>>    @property auto back() {
>>      return agent(r.back);
>>    }
>>    void popBack() {
>>      r.popBack();
>>    }
>>  }
>>
>>  static if (isRandomAccessRange!R) {
>>    auto opIndex(size_t i) {
>>      return agent(r[i]);
>>    }
>>  }
>>
>>  static if (hasLength!R) {
>>    @property auto length() {
>>      return r.length;
>>    }
>>  }
>> }
>
> Compiler? Compiler flags?

dmd v2.067.0

dub --build=release (-release -inline -O -boundscheck=off)
April 24, 2015
On Friday, 24 April 2015 at 10:22:05 UTC, Chris wrote:
> I replaced a range that was similar to map with map and the performance dropped by ~0.5 msec.
>
> The range I used previously is based on Adam's D Cookbook. It is consistently faster than map.
>
> private struct Transformer(alias agent, R) if (isInputRange!R) {
>
>   private R r;
>   this (R r) {
>     this.r = r;
>   }
>
>   static if (isInfinite!R) {
>     enum bool empty = false;
>   }
>   else {
>     @property bool empty() {
>       return r.empty;
>     }
>   }
>
>   void popFront() {
>     r.popFront();
>
>   }
>   @property ref auto front() {
>     return agent(r.front);
>   }
>
>   static if (isForwardRange!R) {
>     @property auto save() {
>       return Transformer!(agent, R)(r.save);
>     }
>   }
>
>   static if (isBidirectionalRange!R) {
>     @property auto back() {
>       return agent(r.back);
>     }
>     void popBack() {
>       r.popBack();
>     }
>   }
>
>   static if (isRandomAccessRange!R) {
>     auto opIndex(size_t i) {
>       return agent(r[i]);
>     }
>   }
>
>   static if (hasLength!R) {
>     @property auto length() {
>       return r.length;
>     }
>   }
> }

That is pretty much identical to the implementation of map, see https://github.com/D-Programming-Language/phobos/blob/master/std/algorithm/iteration.d#L504

The only difference is that you don't implement opSlice. Perhaps slicing is particularly slow for your source range?
April 24, 2015
On Friday, 24 April 2015 at 11:33:48 UTC, John Colvin wrote:
> On Friday, 24 April 2015 at 10:22:05 UTC, Chris wrote:
>> I replaced a range that was similar to map with map and the performance dropped by ~0.5 msec.
>>
>> The range I used previously is based on Adam's D Cookbook. It is consistently faster than map.
>>
>> private struct Transformer(alias agent, R) if (isInputRange!R) {
>>
>>  private R r;
>>  this (R r) {
>>    this.r = r;
>>  }
>>
>>  static if (isInfinite!R) {
>>    enum bool empty = false;
>>  }
>>  else {
>>    @property bool empty() {
>>      return r.empty;
>>    }
>>  }
>>
>>  void popFront() {
>>    r.popFront();
>>
>>  }
>>  @property ref auto front() {
>>    return agent(r.front);
>>  }
>>
>>  static if (isForwardRange!R) {
>>    @property auto save() {
>>      return Transformer!(agent, R)(r.save);
>>    }
>>  }
>>
>>  static if (isBidirectionalRange!R) {
>>    @property auto back() {
>>      return agent(r.back);
>>    }
>>    void popBack() {
>>      r.popBack();
>>    }
>>  }
>>
>>  static if (isRandomAccessRange!R) {
>>    auto opIndex(size_t i) {
>>      return agent(r[i]);
>>    }
>>  }
>>
>>  static if (hasLength!R) {
>>    @property auto length() {
>>      return r.length;
>>    }
>>  }
>> }
>
> That is pretty much identical to the implementation of map, see https://github.com/D-Programming-Language/phobos/blob/master/std/algorithm/iteration.d#L504

Yeah, I saw that.

> The only difference is that you don't implement opSlice. Perhaps slicing is particularly slow for your source range?

The source range is a string[][].
April 24, 2015
> dmd v2.067.0
>
> dub --build=release (-release -inline -O -boundscheck=off)

Does a benchmark of dmd generated code really matter?
April 24, 2015
On Friday, 24 April 2015 at 11:38:46 UTC, Casper Færgemand wrote:
>> dmd v2.067.0
>>
>> dub --build=release (-release -inline -O -boundscheck=off)
>
> Does a benchmark of dmd generated code really matter?

At least for dmd.
April 24, 2015
On Friday, 24 April 2015 at 11:49:24 UTC, Chris wrote:
> On Friday, 24 April 2015 at 11:38:46 UTC, Casper Færgemand wrote:
>>> dmd v2.067.0
>>>
>>> dub --build=release (-release -inline -O -boundscheck=off)
>>
>> Does a benchmark of dmd generated code really matter?
>
> At least for dmd.

dmd is good at making machine code fast, not making fast machine code. It's not that I mind effort is put into dmd, it's just that if the code change doesn't improve the output of gdc and ldc, then code compiled with their better optimizers won't ever feel the change.

Can you post the full benchmark?
April 24, 2015
On Friday, 24 April 2015 at 11:33:48 UTC, John Colvin wrote:
> That is pretty much identical to the implementation of map, see https://github.com/D-Programming-Language/phobos/blob/master/std/algorithm/iteration.d#L504
>
> The only difference is that you don't implement opSlice. Perhaps slicing is particularly slow for your source range?

I suspect the cause is the current inability to inline across modules.