javascript – 如何优化RGB到HSL转换功能的执行时间?
作者:互联网
我已经创建了这个函数来将RGB颜色转换为HSL颜色.它完美无缺.
但我需要让它运行得更快,因为它用于替换画布上的颜色,我需要减少更换的时间.由于图像包含360k像素(600x600px),因此任何东西都可以让它更快.
这是我目前的实施:
/**
* Convert RGB Color to HSL Color
* @param {{R: integer, G: integer, B: integer}} rgb
* @returns {{H: number, S: number, L: number}}
*/
Colorize.prototype.rgbToHsl = function(rgb) {
var R = rgb.R/255;
var G = rgb.G/255;
var B = rgb.B/255;
var Cmax = Math.max(R,G,B);
var Cmin = Math.min(R,G,B);
var delta = Cmax - Cmin;
var L = (Cmax + Cmin) / 2;
var S = 0;
var H = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2*L) - 1));
switch (Cmax) {
case R:
H = ((G - B) / delta) % 6;
break;
case G:
H = ((B - R) / delta) + 2;
break;
case B:
H = ((R - G) / delta) + 4;
break;
}
H *= 60;
}
// Convert negative angles from Hue
while (H < 0) {
H += 360;
}
return {
H: H,
S: S,
L: L
};
};
解决方法:
TL;博士
>在计算之前定义所有内容
>交换机对性能不利
>循环很糟糕
>使用Closure Compiler进行自动优化
> MEMOIZE! (这个在基准测试中不可用,因为它当时只使用一种颜色)
>比较Math.max和Math.min中的对(if-else对于更大的数字更好,据我所见)
基准
基准是非常基本的;我每次都会生成一个随机的RGB颜色,并将其用于测试套装.
所有转换器实现的颜色相同.
我目前正在使用快速计算机,因此您的数字可能会有所不同.
此时很难进一步优化,因为性能会有所不同,具体取决于数据.
最佳化
在一开始就定义结果值的对象.预先为对象分配内存会以某种方式提高性能.
var res = {
H: 0,
S: 0,
L: L
}
// ...
return res;
不使用开关可以轻松提高性能.
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
虽然循环很容易通过以下方式移
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
我还应用了Closure Compiler来删除代码中的冗余操作.
你应该记住结果!
考虑重构函数以使用三个参数,因此可以使用多维哈希映射来记忆缓存.或者您可以尝试使用WeakMap,但此解决方案的性能未知.
见文章Faster JavaScript Memoization For Improved Application Performance
结果
Node.js的
最好的是rgbToHslOptimizedClosure.
node -v
v6.5.0
rgbToHsl x 16,468,872 ops/sec ±1.64% (85 runs sampled)
rgbToHsl_ x 15,795,460 ops/sec ±1.28% (84 runs sampled)
rgbToHslIfElse x 16,091,606 ops/sec ±1.41% (85 runs sampled)
rgbToHslOptimized x 22,147,449 ops/sec ±1.96% (81 runs sampled)
rgbToHslOptimizedClosure x 46,493,753 ops/sec ±1.55% (85 runs sampled)
rgbToHslOptimizedIfElse x 21,825,646 ops/sec ±2.93% (85 runs sampled)
rgbToHslOptimizedClosureIfElse x 38,346,283 ops/sec ±9.02% (73 runs sampled)
rgbToHslOptimizedIfElseConstant x 30,461,643 ops/sec ±2.68% (81 runs sampled)
rgbToHslOptimizedIfElseConstantClosure x 40,625,530 ops/sec ±2.70% (73 runs sampled)
Fastest is rgbToHslOptimizedClosure
Slowest is rgbToHsl_
浏览器
Chrome Version 55.0.2883.95 (64-bit)
rgbToHsl x 18,456,955 ops/sec ±0.78% (62 runs sampled)
rgbToHsl_ x 16,629,042 ops/sec ±2.34% (63 runs sampled)
rgbToHslIfElse x 17,177,059 ops/sec ±3.85% (59 runs sampled)
rgbToHslOptimized x 27,552,325 ops/sec ±0.95% (62 runs sampled)
rgbToHslOptimizedClosure x 47,659,771 ops/sec ±3.24% (47 runs sampled)
rgbToHslOptimizedIfElse x 26,033,751 ops/sec ±2.63% (61 runs sampled)
rgbToHslOptimizedClosureIfElse x 43,430,875 ops/sec ±3.55% (59 runs sampled)
rgbToHslOptimizedIfElseConstant x 33,696,558 ops/sec ±3.97% (58 runs sampled)
rgbToHslOptimizedIfElseConstantClosure x 44,529,209 ops/sec ±3.56% (60 runs sampled)
Fastest is rgbToHslOptimizedClosure
Slowest is rgbToHsl_
自己运行基准测试
请注意,该浏览器将冻结片刻.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var RGB = { R: getRandomInt(0, 255), G: getRandomInt(0, 255), B: getRandomInt(0, 255) }
// http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param {number} r The red color value
* @param {number} g The green color value
* @param {number} b The blue color value
* @return {Array} The HSL representation
*/
function rgbToHsl_(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [ h, s, l ];
}
function rgbToHsl(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(R, G, B);
var Cmin = Math.min(R, G, B);
var delta = Cmax - Cmin;
var L = (Cmax + Cmin) / 2;
var S = 0;
var H = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
switch (Cmax) {
case R:
H = ((G - B) / delta) % 6;
break;
case G:
H = ((B - R) / delta) + 2;
break;
case B:
H = ((R - G) / delta) + 4;
break;
}
H *= 60;
}
// Convert negative angles from Hue
while (H < 0) {
H += 360;
}
return {
H: H,
S: S,
L: L
};
};
function rgbToHslIfElse(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(R, G, B);
var Cmin = Math.min(R, G, B);
var delta = Cmax - Cmin;
var L = (Cmax + Cmin) / 2;
var S = 0;
var H = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
// Convert negative angles from Hue
while (H < 0) {
H += 360;
}
return {
H: H,
S: S,
L: L
};
};
function rgbToHslOptimized(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(Math.max(R, G), B);
var Cmin = Math.min(Math.min(R, G), B);
var delta = Cmax - Cmin;
var S = 0;
var H = 0;
var L = (Cmax + Cmin) / 2;
var res = {
H: 0,
S: 0,
L: L
}
var remainder = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
switch (Cmax) {
case R:
H = ((G - B) / delta) % 6;
break;
case G:
H = ((B - R) / delta) + 2;
break;
case B:
H = ((R - G) / delta) + 4;
break;
}
H *= 60;
}
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
res.H = H;
res.S = S;
return res;
}
function rgbToHslOptimizedIfElse(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(Math.max(R, G), B);
var Cmin = Math.min(Math.min(R, G), B);
var delta = Cmax - Cmin;
var S = 0;
var H = 0;
var L = (Cmax + Cmin) / 2;
var res = {
H: 0,
S: 0,
L: L
}
var remainder = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
res.H = H;
res.S = S;
return res;
}
function rgbToHslOptimizedIfElseConstant(rgb) {
var R = rgb.R * 0.00392156862745;
var G = rgb.G * 0.00392156862745;
var B = rgb.B * 0.00392156862745;
var Cmax = Math.max(Math.max(R, G), B);
var Cmin = Math.min(Math.min(R, G), B);
var delta = Cmax - Cmin;
var S = 0;
var H = 0;
var L = (Cmax + Cmin) * 0.5;
var res = {
H: 0,
S: 0,
L: L
}
var remainder = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
res.H = H;
res.S = S;
return res;
}
function rgbToHslOptimizedIfElseConstantClosure(c) {
var a = .00392156862745 * c.h, e = .00392156862745 * c.f, f = .00392156862745 * c.c, g = Math.max(Math.max(a, e), f), d = Math.min(Math.min(a, e), f), h = g - d, b = c = 0, k = (g + d) / 2, d = {
a: 0,
b: 0,
g: k
};
0 !== h && (c = h / (1 - Math.abs(2 * k - 1)), g === a ? b = (e - f) / h % 6 : g === e ? b = (f - a) / h + 2 : g === f && (b = (a - e) / h + 4), b *= 60);
0 > b && (a = b % 360, 0 !== a && (b = a + 360));
d.a = b;
d.b = c;
return d;
};
function rgbToHslOptimizedClosure(c) {
var a = c.f / 255, e = c.b / 255, f = c.a / 255, k = Math.max(Math.max(a, e), f), d = Math.min(Math.min(a, e), f), g = k - d, b = c = 0, l = (k + d) / 2, d = {
c: 0,
g: 0,
h: l
};
if (0 !== g) {
c = g / (1 - Math.abs(2 * l - 1));
switch (k) {
case a:
b = (e - f) / g % 6;
break;
case e:
b = (f - a) / g + 2;
break;
case f:
b = (a - e) / g + 4;
}
b *= 60;
}
0 > b && (a = b % 360, 0 !== a && (b = a + 360));
d.c = b;
d.g = c;
return d;
}
function rgbToHslOptimizedClosureIfElse(c) {
var a = c.f / 255, e = c.b / 255, f = c.a / 255, g = Math.max(Math.max(a, e), f), d = Math.min(Math.min(a, e), f), h = g - d, b = c = 0, l = (g + d) / 2, d = {
c: 0,
g: 0,
h: l
};
0 !== h && (c = h / (1 - Math.abs(2 * l - 1)), g === a ? b = (e - f) / h % 6 : g === e ? b = (f - a) / h + 2 : g === f && (b = (a - e) / h + 4), b *= 60);
0 > b && (a = b % 360, 0 !== a && (b = a + 360));
d.c = b;
d.g = c;
return d;
}
new Benchmark.Suite()
.add('rgbToHsl', function () {
rgbToHsl(RGB);
})
.add('rgbToHsl_', function () {
rgbToHsl_(RGB.R, RGB.G, RGB.B);
})
.add('rgbToHslIfElse', function () {
rgbToHslIfElse(RGB);
})
.add('rgbToHslOptimized', function () {
rgbToHslOptimized(RGB);
})
.add('rgbToHslOptimizedClosure', function () {
rgbToHslOptimizedClosure(RGB);
})
.add('rgbToHslOptimizedIfElse', function () {
rgbToHslOptimizedIfElse(RGB);
})
.add('rgbToHslOptimizedClosureIfElse', function () {
rgbToHslOptimizedClosureIfElse(RGB);
})
.add('rgbToHslOptimizedIfElseConstant', function () {
rgbToHslOptimizedIfElseConstant(RGB);
})
.add('rgbToHslOptimizedIfElseConstantClosure', function () {
rgbToHslOptimizedIfElseConstantClosure(RGB);
})
// add listeners
.on('cycle', function (event) {
console.log(String(event.target));
})
.on('complete', function () {
console.log('Fastest is ' + this.filter('fastest').map('name'));
console.log('Slowest is ' + this.filter('slowest').map('name'));
})
// run async
.run({ 'async': false });
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdn.rawgit.com/bestiejs/benchmark.js/master/benchmark.js"></script>
标签:hsl,javascript,optimization,rgb 来源: https://codeday.me/bug/20190724/1523887.html