Kitware / vtk-js

Visualization Toolkit for the Web
https://kitware.github.io/vtk-js/
BSD 3-Clause "New" or "Revised" License
1.24k stars 379 forks source link

[Perf] Optimization suggestions for function "halfLut" in Texture.js #2804

Open zzzhello opened 1 year ago

zzzhello commented 1 year ago

High-level description

The function toHalf is very time-consuming, although it has been optimized in version v27.3.0(https://github.com/kitware/vtk-js/commit/f38460cce8bc63b483d30840ce939978ed607420).

I think we can use a lookup table(e.g. halfLut = new Uint16Array(lutSize)) to replace function 'toHalf'. This can save at least twice or more time in chrome(300ms vs 100ms).

Steps to reproduce

Here is the code snippet(function 'updateArrayDataType' is in file '\OpenGL\Texture.js')

var _halfLut = null; //toHalf look up table
function getHalfFloatLut(lutSize,halfLut_offset){
    if(_halfLut){
      return _halfLut;
    }
    _halfLut = new Uint16Array(lutSize);
    for(var i =0;i<lutSize;i++){
      _halfLut[i] = toHalf(i- halfLut_offset);
    }
    return _halfLut;
}
 function updateArrayDataType(dataType, data) {
 //==other codes....===
  if (halfFloat) {
      var useHalfLut = true;
      const halfLut_offset = 2048;//we assume data range is [-2048,2048] 
      const lutSize = 4097;
      var halfLut = getHalfFloatLut(lutSize,halfLut_offset);

      for (var _idx2 = 0; _idx2 < data.length; _idx2++) {
        if (data[_idx2]) {
          var newArray = new Uint16Array(pixCount);
          var src = data[_idx2];
         //===speed up here by halfLut ===
          if(useHalfLut){
            for (var i = 0; i < pixCount; i++) {
              newArray[i] = halfLut[src[i] + halfLut_offset];
            }
          }
          else{
            for (var i = 0; i < pixCount; i++) {
              newArray[i] = toHalf(src[i]);
            }
          }
          pixData.push(newArray);
        } else {
          pixData.push(null);
        }
      }
    } // The output has to be filled
 //===other codes....===
}

Detailed Current Behavior

N/A

Environment

finetjul commented 1 year ago

@sedghi

sedghi commented 1 year ago

Thanks for including me. Why are we setting the halfLut_offset and size to those values? (especially the luSize)

zzzhello commented 1 year ago

'2048' and '4097' is not a good codding habit, I just use them for the example, sorry for that.

  1. Why 'halfLut_offset '? Because 'image data value' can be negative and halfLut's table index should always be positive,we can use 'halfLut_offset ' to map 'image data value' to halfLut's table index. You can use min value or 'scaleOffsets.offset' to replace 'halfLut_offset '

  2. Why 'luSize'? 'luSize' is lut table length, it can be range of the data value( max - min), you can use 'scaleOffsets.scale'

  3. how to get 'scaleOffsets.offset' and 'scaleOffsets.scale' ? We can refer function 'create3DFilterableFromDataArray' in Texture.js, the values are cached ,no need to recompute.

sedghi commented 1 year ago

Thanks for explanation, I guess it makes sense a lot

finetjul commented 1 year ago

@zzzhello would you mind creating a PR ?

zzzhello commented 1 year ago

@zzzhello would you mind creating a PR ?

Sure, i would be happy to.