是否快速复制写入所有结构?

时间:2017-04-19 04:29:37

标签: swift value-type copy-on-write

我知道swift会优化复制写入数组,但它会为所有结构执行此操作吗?例如:

<script>

var dy = {
    instanceAlias : {}
};

var CATEGORIES = ["a1","a2","a3","a4","a5","a6","a7","a8","a9","a10","a11","a12","a13","a14","a15","a16","a17","a18","a19","a20"];

var THEME = "";
var HEIGHT = "290";

var highchartConfig = {
    column : {
        chart: {
            renderTo: null,
            type: 'column'
        },
        xAxis: {
            gridLineWidth: 1,               
            tickLength: 4,
            categories : null,
            labels : {
                rotation: 0,
                formatter : function(d){
                    return dy.instanceAlias[this.value] || this.value;
                }
            }
        },
        yAxis: {
            min : 0,
            max : 5,
            lineWidth: 1,
            tickLength: 4, // Same value as offset
            tickPosition: "outside",    
            tickWidth: 1,
            tickAmount: 3,
            tickInterval : 25,
            title: {
               style : {
                   display: 'none'
               }
            },
            stackLabels: {
                style : {
                    "fontWeight": "normal",
                    color : (THEME == "dark") ? "#b0b0b3" : "#000"
                }       
            }
        },
        plotOptions: {
            series: {
                borderRadius: 6,
                borderWidth: 0,
                marker :{
                    enabled :true,
                    symbol : "circle",
                    radius : 4

                }
            }
        },
        series: null
    }
};


var stack1Config = $.extend(true,{},highchartConfig.column,{
    colors : ["#009651","#ff9800","#ff1100","#009651"], //stack colors 
    chart: {
        animation: false,           
        renderTo: "stack1",
        height : HEIGHT
    },
    xAxis: {
        categories : CATEGORIES
    },
    title: {
        style: {
            display: 'none'
        }
    },
    subtitle: {
        style: {
            display: 'none'
        }
    },
    legend : false,
    tooltip : false,
    yAxis: {
        max : null,
        tickInterval : null,
        tickLength : null,
        stackLabels: {
            enabled: true,
            useHTML : false,
            style : {
                "fontWeight": "normal",
                color : (THEME == "dark") ? "#b0b0b3" : "#000"
            }
        }
    },
    plotOptions: {                  
        column: {
            stacking: 'normal',
        },
        series: {
            animation: false,
            stickyTracking: false,
            enableMouseTracking: false,
            lineWidth: 0,     
            marker: {
                enabled : true,
                symbol : "circle",
                radius : 4
            },      
        }        
    },
    series : (function(){
        var series = new Array();
        var stack =  [           
                {id : "normal",  alias : "normal"},
                {id : "warning", alias : "warning"},
                {id : "danger",  alias : "danger"}
            ];

        for (var i = 0; i < stack.length; i++) {

            //stack column 브러시 추가.
            series.push({
                name : stack[i].alias || stack[i].id,
                id : stack[i].id,
                lineWidth: 1,
                data : (function(categories){

                        var data = [];
                        for (var i = 0; i < categories.length; i++) {
                            data.push(0);
                        }               
                        return data;                
                })(CATEGORIES)
            }); 
        }

        //circle 
        series.push({
            type : "scatter",
            name : "scatter",
            id : "scatter",
            marker: {
                 fillColor: '#FFFFFF',
                 lineWidth: 1,
                 lineColor: null 
            },
            data : (function(b){

                var data = [];
                for (var i = 0; i < b.length; i++) {
                    data.push(0);
                }               
                return data;                
            })(CATEGORIES)
        });     

        console.log(series)

        return series;
    })()
});


var stack2Config = $.extend(true,{},highchartConfig.column,{
    colors :["#009651"],
    chart: {
        animation: false,
        renderTo: "stack2",
        height : HEIGHT
    },
    xAxis: {
        categories : CATEGORIES
    },
    title: {
        style: {
            display: 'none'
        }
    },
    subtitle: {
        style: {
            display: 'none'
        }
    },
    legend : false,
    tooltip : false,
    yAxis: {
        max : null,
        tickInterval : null,
        tickLength : null,
        stackLabels: {
            enabled: true,
            useHTML : false,
            style : {
                "fontWeight": "normal",
                color : (THEME == "dark") ? "#b0b0b3" : "#000"
            }
        }
    },
    plotOptions: {
        animation: false,
        stickyTracking: false,
        enableMouseTracking: false,
        lineWidth: 0,     
        marker : {
            enabled : false
        },              
        column : {
            dataLabels: {
                enabled: true,
                useHTML: true,
                x : 0,
                y : 0,
                style : {
                    "fontWeight": "normal"
                }
            }
        }
    },
    series : (function(){
        var series = new Array();
        var stack =  [           
                {id : "cpu",  alias : "CPU"}
            ];

        for (var i = 0; i < stack.length; i++) {

            //stack column 브러시 추가.
            series.push({
                name : stack[i].alias || stack[i].id,
                id : stack[i].id,
                lineWidth: 1,
                data : (function(categories){

                        var data = [];
                        for (var i = 0; i < categories.length; i++) {
                            data.push(0);
                        }               
                        return data;                
                })(CATEGORIES)
            }); 
        }

        //circle 
        series.push({
            type : "scatter",
            name : "scatter",
            id : "scatter",
            marker: {
                 fillColor: '#FFFFFF',
                 lineWidth: 1,
                 lineColor: null 
            },
            data : (function(b){

                var data = [];
                for (var i = 0; i < b.length; i++) {
                    data.push(0);
                }               
                return data;                
            })(CATEGORIES)
        });     

        console.log(series)

        return series;
    })()
});

var stack1 = Highcharts.chart(stack1Config);
var stack2 = Highcharts.chart(stack2Config);




    var alarmTimeoutId = null;

    var alarmTimeout = function(){

        return setTimeout(function(){
            var ACT_COLUMN = {
                normal  : [],
                warning : [],
                danger  : [],
                scatter : [] // success,warning,danger total value
            };
            var length = 20;

            for (var i = 0; i < length; i++) {  

                var normalCnt  = Math.round(Math.random()*100);
                var warningCnt = Math.round(Math.random()*100);
                var dangerCnt  = Math.round(Math.random()*100);

                ACT_COLUMN.scatter.push({ y: normalCnt+warningCnt+dangerCnt });
                ACT_COLUMN.normal.push(normalCnt);
                ACT_COLUMN.warning.push(warningCnt);
                ACT_COLUMN.danger.push(dangerCnt);  

            }               

            for (var key in ACT_COLUMN) {
                stack1.get(key).setData(ACT_COLUMN[key],false,false);   
            }

            stack1.xAxis[0].setCategories(CATEGORIES);

            ACT_COLUMN.scatter = null;
            ACT_COLUMN.normal = null;
            ACT_COLUMN.warning = null;
            ACT_COLUMN.danger = null;

            ACT_COLUMN = null;

            var cpuData = [];

            for (var i = 0; i < length; i++) {

                var cpu = Math.round(Math.random()*100);
                var color = "#009651";

                if(cpu <= 50){
                    color = "#009651";
                }else if(cpu <= 80){
                    color = "#ff9800";
                }else{
                    color = "#ff1100";
                }

                cpuData.push({y : cpu, color : color });
            }

            var series = stack2.series;
            var seriesLength = series.length;

            for (var i = 0; i < seriesLength; i++) {
                series[i].setData(cpuData,false,false); 
            }           

            stack2.xAxis[0].setCategories(CATEGORIES);
            clearTimeout(alarmTimeoutId);
            alarmTimeoutId = alarmTimeout();

        }, 2000);           
    }

    alarmTimeout();


</script>

1 个答案:

答案 0 :(得分:28)

Array 已实现具有写时复制行为 - 无论编译器优化如何,您都可以获得它(当然,优化可以减少案例数量)副本需要发生)。

在基本级别,Array只是一个结构,它包含对包含元素的堆分配缓冲区的引用 - 因此多个Array实例可以引用相同的缓冲。当你来改变给定的数组实例时,实现将检查缓冲区是否被唯一引用,如果是,则直接改变它。否则,该数组将执行底层缓冲区的副本,以保留值语义。

但是,使用Point结构时,您并未在语言级别实施写时复制。当然,作为@Alexander says,这并不能阻止编译器执行各种优化以最小化复制整个结构的成本。这些优化不需要遵循写时复制的确切行为 - 只要程序根据语言规范运行,编译器就可以随心所欲地执行它想要的任何。 / p>

在您的具体示例中,p1p2都是全局的,因此编译器需要使它们成为不同的实例,因为同一模块中的其他.swift文件可以访问它们(尽管这可能可能通过整个模块优化进行优化)。但是,编译器仍然不需要复制实例 - 它只能evaluate the floating-point addition at compile-time并使用0.0初始化其中一个全局变量,而使用1.0初始化其中一个全局变量。

如果它们是函数中的局部变量,例如:

struct Point {
    var x: Float = 0
}

func foo() {
    var p1 = Point()
    var p2 = p1
    p2.x += 1
    print(p2.x)
}

foo()

编译器甚至不必创建两个Point实例 - 它只能创建一个初始化为1.0的浮点局部变量,并打印出来。

关于将值类型作为函数参数传递,对于足够大的类型和(在结构的情况下)利用其足够属性的函数,编译器can pass them by reference而不是复制。然后被调用者只能在需要时复制它们,例如在需要使用可变副本时。

在通过值传递结构的其他情况下,编译器也可以specialise functions进行复制,以便仅复制函数所需的属性。

以下代码:

struct Point {
    var x: Float = 0
    var y: Float = 1
}

func foo(p: Point) {
    print(p.x)
}

var p1 = Point()
foo(p: p1)

假设foo(p:)没有被编译器内联(在这个例子中,但是一旦它的实现达到一定的大小,编译器就不会认为值得) - 编译器可以将功能专门化为:

func foo(px: Float) {
    print(px)
}

foo(px: 0)

它只会将Point&#39; x属性的值传递给函数,从而节省了复制y属性的成本。

因此编译器将尽其所能以减少值类型的复制。但是在不同情况下进行了如此多的各种优化,你不能简单地将任意值类型的优化行为简化为只写拷贝。